Overview of Data

The purpose of this dataset was to help find predictors of diabetes. The goal is to help medical professionals identify patients with potential risk factors of diabetes.The dataset contains information on patient demographics such as age and gender, as well as medical information including blood glucose levels and hypertension. The observations in this dataset were obtained from research studies and healthcare institutions. The dataset was obtained via Kaggle.

Mustafa, T. Z. (2021). Diabetes Prediction Dataset. Kaggle. https://www.kaggle.com/datasets/iammustafatz/diabetes-prediction-dataset

The dataset consists of 8 predictor variables for diabetes: ‘gender’ the patient’s gender as male or female. ‘age’ the age of the patient in years. ‘hypertension’ yes or no, on whether the patient has hypertension. ‘heart disease’ yes or no, on whether the patient has heart disease. ‘smoking history’ the patient’s smoking history indicated as never, no info, current, former or not current. ‘bmi’ the patient’s body mass index (kg/m**2). ‘HbA1c level’ the patient’s average blood sugar level over the past 2-3 months (%). ‘blood glucose level’ amount of blood sugar in the patient’s blood (mg/dL).

#load in dataset
data <-read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/diabetes_prediction_dataset.csv')
diabetesd<-data

#change heart disease, diabetes and hypertension to 1 and 0 for easier use in MICE.
data$diabetes <- recode(data$diabetes, "1"="Yes", "0"="No")
data$hypertension <- recode(data$hypertension, "1"="Yes", "0"="No")
data$heart_disease <- recode(data$heart_disease, "1"="Yes", "0"="No")

#plot interaction between diabetes and hypertension.
ggplot(data, aes(x = hypertension, fill = diabetes)) +
  geom_bar(position = "dodge") + # Use "stack" for a stacked bar chart
  labs (title="Risk of Diabetes by Hypertension", x="Hypertension", y= "Frequency",
        fill= "Diabetes")
The plot of shows the frequency of patients with diabetes and hypertension. The plot indiactes a higher frequency of patients not having diabetes and not having hypertension. There does not seem to be an interaction between having diabetes and having hypertension based on the low frequency of patients having both.

The plot of shows the frequency of patients with diabetes and hypertension. The plot indiactes a higher frequency of patients not having diabetes and not having hypertension. There does not seem to be an interaction between having diabetes and having hypertension based on the low frequency of patients having both.

Comparison of Two Categorical Variables

The purpose of this graph is to show the relationship between having hypertension and diabetes. For this graph, I changed the values of 1 in both the diabetes and hypertension variable to “Yes” and the value 0 to “No.” These integers were meant to represent categorical variables with 1 correlating to being positive for hypertension or diabetes and 0 correlating to being negative to hypertension or diabetes. Based on our graph, we can see that there is no correlation between having diabetes and having hypertension, with most patients within our sample having neither diabetes or hypertension. Thus, hypertension would not be a good predictor of diabetes within patients.

interact = ggplot(data, aes(x =blood_glucose_level , y = HbA1c_level, group = diabetes, color = diabetes)) +
  geom_line(size = 1) +  # Lines representing interaction
  geom_point(size = 2) + # Points for data
  labs(
    title = "Interaction of Blood Glucose and HbA1c Levels",
    x = "Blood Glucose Level",
    y = "HbA1c Level",
    color = "Presence of Diabetes"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
    axis.text = element_text(size = 12),
    legend.position = "top"
  )
interact
The plot shows the interaction between blood glucose levels and HbA1c levels on diabetes. Based on the plot, higher blood glucose levels and HbA1c levels are positively correlated with the presence of diabetes. In comparison, lower levels, indicate that a patient will more commonly not have diabetes. The height of the points shown for diabetes represent the levels of HbA1c, based on the plot, HbA1c levels predict the presence of diabetes more often than blood glucose levels.

The plot shows the interaction between blood glucose levels and HbA1c levels on diabetes. Based on the plot, higher blood glucose levels and HbA1c levels are positively correlated with the presence of diabetes. In comparison, lower levels, indicate that a patient will more commonly not have diabetes. The height of the points shown for diabetes represent the levels of HbA1c, based on the plot, HbA1c levels predict the presence of diabetes more often than blood glucose levels.

Comparison of Two Numerical Variables

The two variables being compared here are Blood Glucose Level and HbA1c Level. HbA1cc is a shortened term for hemoglobin A1c. Hb1Ac level is a measure of average blood sugar levels over 2-3 months and is measured in %. Blood glucose level is the amount of blood sugar in a patients blood at a given time. Our plot shows a correlation between high levels in HbA1c and blood glucose levels and it’s relationship with diabetes. This plot suggests patients with high levels of HbA1c and blood glucose levels are at high risk for diabetes.

#plot of a numerical and categorical variable
 boxplot(data$age ~ data$diabetes,
         xlab="Patient Age",
         ylab="Presence of Diabetes",
         col = c("skyblue", "purple"),
         main="Visualization of Diabetes by Age",
         cex.main = 1.1,
         col.main = "navy")
The boxplot shows the relationship between age and presence of diabetes. The boxplot shows that older patients on average are more often have diabetes. There are a few outliers in the diabetes population that appear to be under the age of 20 years. This could be representative of patients with Type I diabetes, since the data does not distinguish between Type I and Type II diabetes.

The boxplot shows the relationship between age and presence of diabetes. The boxplot shows that older patients on average are more often have diabetes. There are a few outliers in the diabetes population that appear to be under the age of 20 years. This could be representative of patients with Type I diabetes, since the data does not distinguish between Type I and Type II diabetes.

Comparison of a Numerical and Categorical Variables

This chart shows the relationship between age and diabetes diagnosis. This chart shows that as patients grow older they are at higher risk for diabetes. This would make age a predictor for diabetes, with the average age for those without diabetes being 40 years and 60 years for those with diabetes. However, we can see that we do have a few outliers for those with diabetes with some patients being diagnosed at 20 or younger. This could be from the population of people with Type I diabetes which is usually diagnosed in patients under 20. Our dataset does not distinguish between type I and type II diabetes.

Conclusion for Comparison of Variables

Diabetes can be predicted using age, blood glucose levels, and HbA1c levels in patients. However, our results showed hypertension is not a good predictor of diabetes. Based on our findings, it would be interesting to look at factors that occur once a patient becomes older that could lead to diabetes at an older age. As well, diet could be another factor to look into, such as grams of sugar consumed on a daily or weekly basis, to see if sugar consumption can predict diabetes. Another important thing to analyze in future studies would be Type I versus Type II diabetes, as it could be the reason for some of the outliers in the boxplot.

Overview of Missing Variables

An important step in data analysis for machine learning is handling missing values. Missing values in a dataset can occur for many reasons, but not limited to non-response in surveys, participants leaving the study, data entry errors and system limitations. If missing values are not addressed, models can become biased and accuracy of the anaylysis can decrease. This section examines different imputation strategies to effectively handle missing values.

The imputation methods we will examine include:

Replacement Imputation for Categorical Features: Mode imputation and KNN-based imputation.

Regression-based Imputation for Numerical Features: Predictive modeling to estimate missing values.

Multiple Imputation: Advanced methods such as MICE to improve robustness.

# create random observation ID and replace the corresponding obs with missing
ltr.missing.id <- sample(1:1000, 50, replace = FALSE)
gpa.missing.id <- sample(1:1000, 15, replace = FALSE) 
diabetesd$bmi[ltr.missing.id] <- NA
diabetesd$heart_disease[gpa.missing.id] <- NA
diabetesd$smoking_history[diabetesd$smoking_history == "No Info"] <- NA

Mode Imputation for Categorical Variables

In this method, missing variables are replaced with the most frequent category of the corresponding variable. Mode imputation ensures consistency and works well when missing values are randomly distributed. Below, mode imputation is used on the variable, heart disease, which is 1 if the patient has heart disease and 0 if the patient does not have heart disease.The variable smoking history had current, former, no info, never, and ever as possible values. No Info indicates that we do not have information on the participant’s smoking history and will be treated as a missing values.

diabetesd <-diabetesd
# Function to impute mode

Mode <- function(x) {
  ux <- unique(na.omit(x))  # Remove NAs before computing mode
  tab <- tabulate(match(x, ux))
  mode_value <- ux[which.max(tab)]
  
  # Ensure mode_value is of the same type as x
  if (is.factor(x)) {
    return(factor(mode_value, levels = levels(x)))
  } else if (is.character(x)) {
    return(as.character(mode_value))
  } else {
    return(as.numeric(mode_value))
  }
}
#apply mode imputation
value_imputed <- data.frame(
  original = diabetesd$heart_disease,
  original2=diabetesd$smoking_history,
  imputed_modehd = replace(diabetesd$heart_disease, is.na(diabetesd$heart_disease), Mode((diabetesd$heart_disease))),
  imputed_modesh = replace(diabetesd$smoking_history, is.na(diabetesd$smoking_history), Mode(diabetesd$smoking_history)))
summary(value_imputed)
    original        original2         imputed_modehd    imputed_modesh    
 Min.   :0.00000   Length:100000      Min.   :0.00000   Length:100000     
 1st Qu.:0.00000   Class :character   1st Qu.:0.00000   Class :character  
 Median :0.00000   Mode  :character   Median :0.00000   Mode  :character  
 Mean   :0.03943                      Mean   :0.03942                     
 3rd Qu.:0.00000                      3rd Qu.:0.00000                     
 Max.   :1.00000                      Max.   :1.00000                     
 NA's   :15                                                               
#plot the comparison of original data and mode imputed data
h1 <- ggplot(value_imputed, aes(x = original)) +
  geom_histogram(bins=10, fill = "#ad1538", color = "#000000", position = "identity") +
  ggtitle("Original for Heart Disease") +
  theme_classic()
h2 <- ggplot(value_imputed, aes(x = original2)) +
  geom_bar( fill = "#15ad4f", color = "#000000", position = "identity") +
  ggtitle("Original for Smoking History") +
  theme_classic()
h3 <- ggplot(value_imputed, aes(x = imputed_modehd)) +
  geom_histogram(bins=10, fill = "#1543ad", color = "#000000", position = "identity") +
  ggtitle("Mode-imputed for Heart Disease") +
  theme_classic()
h4 <- ggplot(value_imputed, aes(x = imputed_modesh)) +
  geom_bar( fill = "#ad8415", color = "#000000", position = "identity") +
  ggtitle("Mode-imputed for Smoking History") +
  theme_classic()


plot_grid(h1, h2, h3, h4, nrow = 2, ncol = 2)
<img src=“First-Project_files/figure-html/unnamed-chunk-5-1.png” alt=“The plot shows the comparison between the original data containing missing values for the variables heart disease and smoking history, and the data after using mode impute. The histograms show that mode impute used what was most common to replace the missing values. This is seen clearly in the variable smoking history, where”never” went from originally a count of about 40,000 to about 70,000. Heart disease had a small amount of missing data (only 15), however the 15 missing were changed to 0, indicating the patient would not have heart disease.” width=“672” />

The plot shows the comparison between the original data containing missing values for the variables heart disease and smoking history, and the data after using mode impute. The histograms show that mode impute used what was most common to replace the missing values. This is seen clearly in the variable smoking history, where “never” went from originally a count of about 40,000 to about 70,000. Heart disease had a small amount of missing data (only 15), however the 15 missing were changed to 0, indicating the patient would not have heart disease.

Regression-Based Imputation for Numerical Variables

Using regression models, we can estimate missing values. For the variable, bmi, we can predict the missing values using estimates from our model. Bmi is a continuous variable, unlike the previous example where our values could only be 0 or 1. In this method, a linear model was created to help create estimates of our values.

#create a dataset for regression impute
regimpute<-diabetesd

# Identify rows where BMI is missing
missing_rows <- which(is.na(diabetesd$bmi))  # Get row indices


# Train a linear model using complete cases
lm_model <- lm(bmi ~ age + blood_glucose_level + HbA1c_level + heart_disease + hypertension + diabetes, 
               data = regimpute, na.action = na.exclude)

# Impute missing BMI values using the model
diabetesd$bmi[missing_rows] <- predict(lm_model, newdata = regimpute[missing_rows, ])


dep.mi <- bind_rows(
  data.frame(value = diabetesd$bmi, imputed = regimpute$bmi))
             #imp = "dep.imp1")


i7<-boxplot(diabetesd$bmi,
main = "Mean bmi in Original Data",
xlab = "BMI",
col = "blue",
border = "black",
horizontal = TRUE,
notch = TRUE
)
The boxplots show the comparison of the variable bmi before and after regression imputation.

The boxplots show the comparison of the variable bmi before and after regression imputation.

i8 <-boxplot(regimpute$bmi,
main = "Regression Imputation for BMI",
xlab = "BMI",
col = "red",
border = "black",
horizontal = TRUE,
notch = TRUE
)
The boxplots show the comparison of the variable bmi before and after regression imputation.

The boxplots show the comparison of the variable bmi before and after regression imputation.

MICE Imputation

MICE is multiple imputation techniques used to impute missing values. This method also uses regression models and accounts for uncertainty by generating multiple possible values. Using MICE, the variables smoking history, heart disease, and bmi, can be imputated within one function. MICE can handle different types of variables, imputates based on the relationship between variables and it accounts for variability in missing data.

#reload dataset so that it is free of mode and regression imputation done previously
data2 <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/diabetes_prediction_dataset.csv')

#create missing values and make smoking history a binary variable
ltr.missing.id <- sample(1:1000, 50, replace = FALSE)
gpa.missing.id <- sample(1:1000, 15, replace = FALSE) 
data2$smoking_history[data2$smoking_history== "No Info"] <-NA
data2$bmi[ltr.missing.id] <- NA
data2$heart_disease[gpa.missing.id] <- NA

#make variables numeric
data2$gender <- as.numeric(as.factor(data2$gender))
data2$heart_disease<- as.factor(as.numeric(data2$heart_disease))
data2$smoking_history<-as.factor(data2$smoking_history)
#apply MICE imputation
df_mice <- mice(data2, method = c("","","","pmm","polyreg","pmm","","",""),  m = 20, maxit=20,seed = 123, print=F)

# Complete dataset with imputed values
df_imputed <- complete(df_mice)

#plot comparisons of before and after MICE imputation for heart disease
g1 <- ggplot(value_imputed, aes(x = imputed_modehd)) +
  geom_bar(fill = "#1543ad", color = "#000000", position = "identity") +
  ggtitle("Mode-imputed") +
  labs(x="Heart Disease")+
  theme_classic()
g2 <- ggplot(value_imputed, aes(x = original)) +
  geom_bar(fill = "#ad1538", color = "#000000", position = "identity") +
  ggtitle("Original") +
  labs(x="Heart Disease")+
  theme_classic()
g3 <- ggplot(df_imputed, aes(x = heart_disease)) +
  geom_bar(fill = "purple", color = "#000000", position = "identity") +
  ggtitle("MICE") +
  labs(x="Heart Disease")+
  theme_classic()
plot_grid(g1, g2, g3, nrow=1, ncol =3)
Based on the three graphs, MICE seems to have produced values that appear almost identical to the original dataset. Mode impute seems to be slightly different since it relies on filling missing values with the most common value. In this case, mode impute filled all missing values as 0 and under-represented 1.

Based on the three graphs, MICE seems to have produced values that appear almost identical to the original dataset. Mode impute seems to be slightly different since it relies on filling missing values with the most common value. In this case, mode impute filled all missing values as 0 and under-represented 1.

#plot comparisons for smoking history
g4 <- ggplot(df_imputed, aes(x = smoking_history)) +
  geom_bar( fill = "purple", color = "#000000", position = "identity") +
  ggtitle("MICE") +
  labs(x="Smoking History")+
  theme_classic()
g5 <- ggplot(value_imputed, aes(x = original2)) +
  geom_bar( fill = "red", color = "#000000", position = "identity") +
  ggtitle("Original Smoking History") +
  labs(x="Smoking History")+
  theme_classic()
g6 <- ggplot(value_imputed, aes(x = imputed_modesh)) +
  geom_bar( fill = "blue", color = "#000000", position = "identity") +
  ggtitle("Mode-imputed") +
  labs(x="Smoking History")+
  theme_classic()
plot_grid(g4,g5,g6, nrow=3, ncol=1)
<img src=“First-Project_files/figure-html/unnamed-chunk-8-1.png” alt=“The bar graphs show the comparison of the original data to mode imputation and MICE. MICE appears to have dispersed the data more evenly across the different categories, while mode imputation has only added values to”never.” width=“672” />

The bar graphs show the comparison of the original data to mode imputation and MICE. MICE appears to have dispersed the data more evenly across the different categories, while mode imputation has only added values to “never.

#plot comparisons for bmi
g7<-boxplot(diabetesd$bmi,
main = "Mean bmi in Original Data",
xlab = "BMI",
col = "blue",
border = "black",
horizontal = TRUE,
notch = TRUE
)
The boxplots show the comparison of the original data to both MICE and regression imputation.

The boxplots show the comparison of the original data to both MICE and regression imputation.

g8 <-boxplot(regimpute$bmi,
main = "Regression Imputation for BMI",
xlab = "BMI",
col = "red",
border = "black",
horizontal = TRUE,
notch = TRUE
)
The boxplots show the comparison of the original data to both MICE and regression imputation.

The boxplots show the comparison of the original data to both MICE and regression imputation.

g9 <-boxplot(df_imputed$bmi,
main = "MICE for BMI",
xlab = "BMI",
col = "purple",
border = "black",
horizontal = TRUE,
notch = TRUE
)
The boxplots show the comparison of the original data to both MICE and regression imputation.

The boxplots show the comparison of the original data to both MICE and regression imputation.

model5 <- with(df_mice, lm(diabetes ~ bmi+ heart_disease+ smoking_history ))
summary(pool(model5))
                        term      estimate    std.error    statistic         df
1                (Intercept) -1.588938e-01 0.0042264348 -37.59524385  8765.6364
2                        bmi  8.387455e-03 0.0001291007  64.96833640 95903.5486
3             heart_disease1  2.213973e-01 0.0044025602  50.28829659 97099.1099
4        smoking_historyever  4.374612e-03 0.0044673218   0.97924717   768.3395
5      smoking_historyformer  3.923135e-02 0.0034242201  11.45701814  1528.3749
6       smoking_historynever -3.066871e-05 0.0026838513  -0.01142713   847.0962
7 smoking_historynot current  6.032198e-03 0.0038465169   1.56822334   577.2461
        p.value
1 6.484235e-287
2  0.000000e+00
3  0.000000e+00
4  3.277661e-01
5  3.251856e-29
6  9.908854e-01
7  1.173770e-01

Skewness

Skewness is a measure of symmetry within a distribution. The skewness measurements below are of the variables age, bmi, HbA1c level, and blood glucose level, respectively. When skewness is equal to 0, we can assume the data is normally distributed. When skewness is greater than 0, our data is positively skewed, while a value less than zero indicates negatively skewed data. For the diabetes dataset, the variables bmi and blood glucose level are 1 or about 1, which indicate a significant right skew in the distribution.

age1<- skewness(df_imputed$age) 
bmi1<-skewness(df_imputed$bmi) 
HbA1c1<-skewness( df_imputed$HbA1c_level) 
glucose1<-skewness(df_imputed$blood_glucose_level)

mytable<-data.frame(age1,bmi1,HbA1c1, glucose1)
kable(mytable, caption= "Skewness of Numeric Variables")
Skewness of Numeric Variables
age1 bmi1 HbA1c1 glucose1
-0.0519782 1.041976 -0.0668528 0.8216426

Transforming Data

To handle our skewed variables, we can normalize the variables bmi and blood glucose level. Normalization rescales data into a fixed range of [0,1]. By normalizing our data, we can fix skewness and create normally distributed data. The variables, age and HbA1c level have been standardized to keep the distribution centered. Standardization is used to create a mean of 0 and a standard deviation of 1, to follow a normal distribution. Based on the results below, we can see that the transformation has corrected the right skew in the data from before.

df_trans <- df_imputed  # Copy original dataset after MICE

#ensure diabetes is numeric and a factor
df_trans$diabetes<- as.numeric(as.factor(df_trans$diabetes))
# Min-Max Normalization
normalize <- function(x) {
  if (max(x) - min(x) == 0) return(rep(0, length(x)))  # Prevent division by zero
  return((x - min(x)) / (max(x) - min(x)))
}

#apply normalization
df_trans$bmi <- normalize(df_imputed$bmi)
df_trans$blood_glucose_level <- normalize(df_imputed$blood_glucose_level)

# Z-Score Standardization
standardize <- function(x) {
  return((x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE))
}

#apply standardization
df_trans$age <- standardize(df_trans$age)
df_trans$HbA1c_level <- standardize(df_trans$HbA1c_level)

# Apply Log Transformation to Fix Skewness (if necessary)
df_trans$bmi <- log1p(df_trans$bmi)  # log(1 + x) avoids log(0) issues
df_trans$blood_glucose_level <- log1p(df_trans$blood_glucose_level)

bmi2 <- skewness(df_trans$bmi)
age2 <- skewness(df_trans$age)
HbA1c2<- skewness(df_trans$HbA1c_level)
glucose2<- skewness(df_trans$blood_glucose_level)

newtable<-data.frame(bmi2,age2,HbA1c2,glucose2)
kable(newtable, caption="Newly Transformed Data")
Newly Transformed Data
bmi2 age2 HbA1c2 glucose2
0.673108 -0.0519782 -0.0668528 0.2867457

Transforming our data has successfully decreased skewness in our variables. We can see bmi and blood glucose level are no longer about 1.

# Combine old and new data for comparison
df_compare <- data.frame(
  bmi_original = df_imputed$bmi,
  bmi_transformed = df_trans$bmi,
  blood_glucose_original = df_imputed$blood_glucose_level,
  blood_glucose_transformed = df_trans$blood_glucose_level
)

# Reshape data for ggplot
df_long <- melt(df_compare)

# Plot histograms
ggplot(df_long, aes(x = value, fill = variable)) +
  geom_histogram(alpha = 0.6, bins = 30, position = "identity") +
  facet_wrap(~ variable, scales = "free") +
  theme_minimal() +
  labs(title = "Comparison of Original vs. Transformed Data", x = "Value", y = "Count")
We can see in the graphs above that after transforming the dataset, the data seems to follow a normal distribution more closely.This is apparent in the histograms appearing more in a bell shape curve.

We can see in the graphs above that after transforming the dataset, the data seems to follow a normal distribution more closely.This is apparent in the histograms appearing more in a bell shape curve.

Analyzing Data

The next step is to find which variables are significantly important when trying to predict diabetes. By using feature selection, we are able to predict possible best fit models for our variable, diabetes. For this data, we will focus on logistic regression. The variables of interest, diabetes, is a binary variable indicating linear regression would not make an ideal model.

Random Forest First, we will look at the best model selected by feature selection through random forest:

# Prepare the data
df_trans$diabetes <- as.factor(df_trans$diabetes)


# Set up RFE control with random forest
control <- rfeControl(functions = rfFuncs, method = "cv", number = 10)

# Apply RFE to identify top features
results <- rfe(
  x = df_trans[, !colnames(df_trans) %in% "diabetes"],
  y = df_trans$diabetes,
  sizes = c(1:9),
  rfeControl = control
)

# Print the selected features
models<-data.frame(predictors(results))
kable(models, caption="Random Forest Selected Model")
Random Forest Selected Model
predictors.results.
HbA1c_level
blood_glucose_level
bmi
heart_disease
age
hypertension
gender
smoking_history

Stepwise Logistic Regression Next, let’s look and stepwise logistic regression

#Stepwise Logistic Regression
# Fit a full logistic regression model with all predictors
full_model <- glm(diabetes ~ ., data = df_trans, family = binomial)

# Perform stepwise selection (default is backward elimination)
step_model1 <- step(full_model, direction = "both")
Start:  AIC=23047.57
diabetes ~ gender + age + hypertension + heart_disease + smoking_history + 
    bmi + HbA1c_level + blood_glucose_level

                      Df Deviance   AIC
- smoking_history      4    23029 23045
<none>                      23024 23048
- gender               1    23078 23100
- heart_disease        1    23176 23198
- hypertension         1    23308 23330
- bmi                  1    24361 24383
- age                  1    25096 25118
- blood_glucose_level  1    30572 30594
- HbA1c_level          1    34856 34878

Step:  AIC=23044.52
diabetes ~ gender + age + hypertension + heart_disease + bmi + 
    HbA1c_level + blood_glucose_level

                      Df Deviance   AIC
<none>                      23029 23045
+ smoking_history      4    23024 23048
- gender               1    23087 23101
- heart_disease        1    23184 23198
- hypertension         1    23314 23328
- bmi                  1    24369 24383
- age                  1    25193 25207
- blood_glucose_level  1    30582 30596
- HbA1c_level          1    34865 34879
summary(step_model1)

Call:
glm(formula = diabetes ~ gender + age + hypertension + heart_disease + 
    bmi + HbA1c_level + blood_glucose_level, family = binomial, 
    data = df_trans)

Coefficients:
                    Estimate Std. Error z value Pr(>|z|)    
(Intercept)         -9.99142    0.11057 -90.360  < 2e-16 ***
gender               0.27068    0.03536   7.654 1.94e-14 ***
age                  1.03388    0.02428  42.583  < 2e-16 ***
hypertension         0.80228    0.04665  17.198  < 2e-16 ***
heart_disease1       0.76240    0.06014  12.677  < 2e-16 ***
bmi                 10.23780    0.28095  36.439  < 2e-16 ***
HbA1c_level          2.49236    0.03769  66.122  < 2e-16 ***
blood_glucose_level 10.29954    0.14902  69.113  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 58163  on 99999  degrees of freedom
Residual deviance: 23029  on 99992  degrees of freedom
AIC: 23045

Number of Fisher Scoring iterations: 8

Both selection types selected the same model! However I will still go through the steps of cross-validation as though I was comparing the two models.

Cross-Validation

Let’s explore cross-validation to see which model is best fit. For logistic regression, it is meaningful to look at accuracy and kappa.

#add classification
df_trans$diabetes<- factor(ifelse(df_trans$diabetes==1, "Yes","No"))

train_control <- trainControl(
  method = "cv",         # cross-validation
  number = 10,           # 10-fold
  classProbs = TRUE # use if you're tracking AUC, Sensitivity, etc.
)

#run CV model
cv_model <- train(
  diabetes ~ hypertension + gender +age + HbA1c_level + blood_glucose_level + heart_disease + bmi, 
  data=df_trans, 
  method = "glm", 
  family = "binomial", 
  trControl = train_control
  )

print(cv_model)
Generalized Linear Model 

1e+05 samples
7e+00 predictors
2e+00 classes: 'No', 'Yes' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 90000, 90000, 90000, 90000, 90000, 90000, ... 
Resampling results:

  Accuracy  Kappa    
  0.95935   0.7015624

Accuracy shows the overall correctness of the model predictions and kappa measures the levels of agreement between variables. Values closer to 1 suggest higher accuracy and better agreement. This model suggests high accuracy and high agreement based on the values.

ROC Curve and AOC Curve

Confusion Matrices ROC is also known as Receiver Operating Characteristic Analysis. It is a technique using graphs that evaluates the performance of a binary model. Before plotting the ROC curve, it is necessary to calculate the true positive rate (TPR) and false positive rate (FPR). Below, are confusion matrices that were used to calculate those values.

#ROC and AUC
df_trans$diabetes <- as.factor(df_trans$diabetes)

# fit a logistic
model.logit <- glm(diabetes ~ age + gender+ bmi+hypertension + heart_disease + blood_glucose_level+ HbA1c_level , family = binomial, data = df_trans)
# predict probability of P(Y = "Yes")
probabilities <- round(as.vector(predict(model.logit, type = "response")),3)
#
thresholds <- c(0.0, 0.25, 0.5, 0.75, 1.0)

# Loop through thresholds and create confusion matrices
for (threshold in thresholds) {
  cat("\nConfusion Matrix for Threshold =", threshold, "\n")
  
  # Convert probabilities to predictions
  # am: 1 = manual transmission, 0 = automatic transmission
  predictions <- ifelse(probabilities > threshold, "Yes", "No")
    all_levels <- union(levels(factor(df_trans$diabetes)), levels(factor(predictions)))
    # Generate confusion matrix
  cm <- confusionMatrix(factor(predictions, levels=all_levels), factor(df_trans$diabetes,levels=all_levels), positive = "Yes")
  print(cm$table)
}

Confusion Matrix for Threshold = 0 
          Reference
Prediction    No   Yes
       No    354     0
       Yes  8146 91500

Confusion Matrix for Threshold = 0.25 
          Reference
Prediction    No   Yes
       No   4216   146
       Yes  4284 91354

Confusion Matrix for Threshold = 0.5 
          Reference
Prediction    No   Yes
       No   5307   867
       Yes  3193 90633

Confusion Matrix for Threshold = 0.75 
          Reference
Prediction    No   Yes
       No   6322  3340
       Yes  2178 88160

Confusion Matrix for Threshold = 1 
          Reference
Prediction    No   Yes
       No   8500 91500
       Yes     0     0

ROC Curve

#create ROC curve

TPR = c(1,91500/(91500+0), 91356/(91356+144), 90632/(90632+868), 88160/(88160+3340), 0/(91500+0))
FPR = c(1,352/(352+8148), 4282/(4282+4218), 3193/(5307+3193), 2178/(2178+6322), 0/(8500+0))
plot(FPR, TPR, type = "b", main = "An Illustrative ROC Curve", col ="blue",
     xlab="1 - Specifity (FPR)", ylab = "Sensitivity (TPR)")
# add a off-diagonal representing random guess algorithm in binary prediction
abline(0,1, lty = 2, col = "red")
# legend
legend("bottomright", c("Logistic Model", "Random Guess"),
       col=c("blue", "red"), lty = 1:2, bty="n", cex = 0.9)

AUC (Area under the Curve) AUC quantifies the performance of the ROC curve into a single value that ranges from 0 to 1. The plot below uses the Riemann Sum approximation to estimate the area under the curve.

#AUC
TPR = round(c(1,91500/(91500+0), 91356/(91356+144), 90632/(90632+868), 88160/(88160+3340), 0/(91500+0)),3)
FPR = round(c(1,352/(352+8148), 4282/(4282+4218), 3193/(5307+3193), 2178/(2178+6322), 0/(8500+0)),3)
TPR0 = TPR[7:1]
FPR0 = FPR[7:1]
#plot(FPR0, TPR0, type = "b")
datSenSpe = data.frame(TPR0, FPR0)
ggROC = ggplot(data = datSenSpe, aes(x = FPR0, y=TPR0)) +
        geom_line(col = "steelblue") +
        geom_point(col = "red") +
        geom_segment(x = FPR0, y = 0, xend = FPR0, yend = TPR0, color = 4) +
        geom_segment(x = 0, y = 0, xend = FPR0[7], yend = 0, color = 6) +
        ggtitle("Approximating the AUC of Logistic Model") +
        xlab("1-specificity (FPR)") + 
        ylab("Sensitivity (TPR)") +
        annotate("text", x = 0.025, y = 0.125, label= "A") + 
        annotate("text", x = 0.105, y = 0.5, label = "B") +
        annotate("text", x = 0.605, y = 0.5, label = "C") +
        theme(plot.title = element_text(hjust = 0.5),
              legend.position = c(0.8, 0.2),
              plot.margin = unit(c(0.15, 0.15, 0.75, 0.15), "inches"),
              axis.line = element_line(size = 2, colour = "navy", linetype=1))
# partition the region under the ROC into trapezoids
ggplotly(ggROC)

AUC Value

A<-(0.041*1)/2
B<- ((0.504-0.041)*1)
C<- ((1-0.504)*1)

AUC<- A+B+C
kable(AUC, caption= "Area under the Curve")
Area under the Curve
x
0.9795

Conclusion for Model Fit

Our best fit model is : \[ \text{ Diabetes} = 9.99 - 0.27\times \text{gender} - 1.03\times \text{age} -0.8\times \text{hypertension} - 0.76\times \text{heart disease} -10.24\times \text{bmi} - 2.49\times \text{HbA1c level} - -10.3\times \text{blood glucose level} \]

The best fit model was selected based on a variety of different features. Both stepwise selection and random forest selected the model as best fit. After running performance analyses on the model, the model was shown to be of high performance. This means that the variables chosen for the final model are significant for predicting diabetes. The model chose not to select smoking history as significant predictor for the response variable. A possible suggestion for this would be to reduce the number of response values to a binary response. Such as, “Have you ever smoked?” and have the response be “Yes” or “No”. This could help in predicting the relationship between smoking and diabetes.

LS0tCnRpdGxlOiAiQXBwbGllZCBNYWNoaW5lIExlYXJuaW5nIGZvciBEYXRhIEFuYWx5c2lzIgphdXRob3I6ICJHYWJyaWVsbGEgS2hhbGlsIgpkYXRlOiAiMjAyNS0wMi0xOCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCgpgYGB7PWh0bWx9Cgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovCgpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLwogIGZvbnQtc2l6ZTogMjJweDsKICBmb250LXdlaWdodDogYm9sZDsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwp9Cmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogbmF2eTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IERhcmtCbHVlOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LXdlaWdodDogYm9sZDsKfQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAyMnB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBkYXJrcmVkOwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9CgpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKPC9zdHlsZT4KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IAojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikKICAgbGlicmFyeShrbml0cikKfQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQp9CmlmICghcmVxdWlyZSgiR0dhbGx5IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikKbGlicmFyeShHR2FsbHkpCn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiAKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQogICAgICAgICAgICAgICAgICAgICAgKSAgCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KFZJTSkgCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoSG1pc2MpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkoRGVzY1Rvb2xzKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkobWljZSkKbGlicmFyeShtb21lbnRzKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHJlc2hhcGUyKSAgIyBGb3IgZGF0YSByZXNoYXBpbmcKbGlicmFyeShjYXJldCkKbGlicmFyeShwUk9DKQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeShmYXN0RHVtbWllcykKYGBgCiMgT3ZlcnZpZXcgb2YgRGF0YQoKVGhlIHB1cnBvc2Ugb2YgdGhpcyBkYXRhc2V0IHdhcyB0byBoZWxwIGZpbmQgcHJlZGljdG9ycyBvZiBkaWFiZXRlcy4gVGhlIGdvYWwgaXMgdG8gaGVscCBtZWRpY2FsIHByb2Zlc3Npb25hbHMgaWRlbnRpZnkgcGF0aWVudHMgd2l0aCBwb3RlbnRpYWwgcmlzayBmYWN0b3JzIG9mIGRpYWJldGVzLlRoZSBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIHBhdGllbnQgZGVtb2dyYXBoaWNzIHN1Y2ggYXMgYWdlIGFuZCBnZW5kZXIsIGFzIHdlbGwgYXMgbWVkaWNhbCBpbmZvcm1hdGlvbiBpbmNsdWRpbmcgYmxvb2QgZ2x1Y29zZSBsZXZlbHMgYW5kIGh5cGVydGVuc2lvbi4gVGhlIG9ic2VydmF0aW9ucyBpbiB0aGlzIGRhdGFzZXQgd2VyZSBvYnRhaW5lZCBmcm9tIHJlc2VhcmNoIHN0dWRpZXMgYW5kIGhlYWx0aGNhcmUgaW5zdGl0dXRpb25zLiBUaGUgZGF0YXNldCB3YXMgb2J0YWluZWQgdmlhIEthZ2dsZS4gCgpNdXN0YWZhLCBULiBaLiAoMjAyMSkuICpEaWFiZXRlcyBQcmVkaWN0aW9uIERhdGFzZXQqLiBLYWdnbGUuIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvaWFtbXVzdGFmYXR6L2RpYWJldGVzLXByZWRpY3Rpb24tZGF0YXNldAoKVGhlIGRhdGFzZXQgY29uc2lzdHMgb2YgOCBwcmVkaWN0b3IgdmFyaWFibGVzIGZvciBkaWFiZXRlczoKJ2dlbmRlcicgdGhlIHBhdGllbnQncyBnZW5kZXIgYXMgbWFsZSBvciBmZW1hbGUuCidhZ2UnIHRoZSBhZ2Ugb2YgdGhlIHBhdGllbnQgaW4geWVhcnMuCidoeXBlcnRlbnNpb24nIHllcyBvciBubywgb24gd2hldGhlciB0aGUgcGF0aWVudCBoYXMgaHlwZXJ0ZW5zaW9uLgonaGVhcnQgZGlzZWFzZScgeWVzIG9yIG5vLCBvbiB3aGV0aGVyIHRoZSBwYXRpZW50IGhhcyBoZWFydCBkaXNlYXNlLgonc21va2luZyBoaXN0b3J5JyB0aGUgcGF0aWVudCdzIHNtb2tpbmcgaGlzdG9yeSBpbmRpY2F0ZWQgYXMgbmV2ZXIsIG5vIGluZm8sIGN1cnJlbnQsIGZvcm1lciBvciBub3QgY3VycmVudC4KJ2JtaScgdGhlIHBhdGllbnQncyBib2R5IG1hc3MgaW5kZXggKGtnL20qKjIpLgonSGJBMWMgbGV2ZWwnIHRoZSBwYXRpZW50J3MgYXZlcmFnZSBibG9vZCBzdWdhciBsZXZlbCBvdmVyIHRoZSBwYXN0IDItMyBtb250aHMgKCUpLgonYmxvb2QgZ2x1Y29zZSBsZXZlbCcgYW1vdW50IG9mIGJsb29kIHN1Z2FyIGluIHRoZSBwYXRpZW50J3MgYmxvb2QgKG1nL2RMKS4KCgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgcGxvdCBvZiBzaG93cyB0aGUgZnJlcXVlbmN5IG9mIHBhdGllbnRzIHdpdGggZGlhYmV0ZXMgYW5kIGh5cGVydGVuc2lvbi4gVGhlIHBsb3QgaW5kaWFjdGVzIGEgaGlnaGVyIGZyZXF1ZW5jeSBvZiBwYXRpZW50cyBub3QgaGF2aW5nIGRpYWJldGVzIGFuZCBub3QgaGF2aW5nIGh5cGVydGVuc2lvbi4gVGhlcmUgZG9lcyBub3Qgc2VlbSB0byBiZSBhbiBpbnRlcmFjdGlvbiBiZXR3ZWVuIGhhdmluZyBkaWFiZXRlcyBhbmQgaGF2aW5nIGh5cGVydGVuc2lvbiBiYXNlZCBvbiB0aGUgbG93IGZyZXF1ZW5jeSBvZiBwYXRpZW50cyBoYXZpbmcgYm90aC4gJ30KI2xvYWQgaW4gZGF0YXNldApkYXRhIDwtcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9HYWJieUs4L0RhdGFzZXRzL3JlZnMvaGVhZHMvbWFpbi9kaWFiZXRlc19wcmVkaWN0aW9uX2RhdGFzZXQuY3N2JykKZGlhYmV0ZXNkPC1kYXRhCgojY2hhbmdlIGhlYXJ0IGRpc2Vhc2UsIGRpYWJldGVzIGFuZCBoeXBlcnRlbnNpb24gdG8gMSBhbmQgMCBmb3IgZWFzaWVyIHVzZSBpbiBNSUNFLgpkYXRhJGRpYWJldGVzIDwtIHJlY29kZShkYXRhJGRpYWJldGVzLCAiMSI9IlllcyIsICIwIj0iTm8iKQpkYXRhJGh5cGVydGVuc2lvbiA8LSByZWNvZGUoZGF0YSRoeXBlcnRlbnNpb24sICIxIj0iWWVzIiwgIjAiPSJObyIpCmRhdGEkaGVhcnRfZGlzZWFzZSA8LSByZWNvZGUoZGF0YSRoZWFydF9kaXNlYXNlLCAiMSI9IlllcyIsICIwIj0iTm8iKQoKI3Bsb3QgaW50ZXJhY3Rpb24gYmV0d2VlbiBkaWFiZXRlcyBhbmQgaHlwZXJ0ZW5zaW9uLgpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBoeXBlcnRlbnNpb24sIGZpbGwgPSBkaWFiZXRlcykpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpICsgIyBVc2UgInN0YWNrIiBmb3IgYSBzdGFja2VkIGJhciBjaGFydAogIGxhYnMgKHRpdGxlPSJSaXNrIG9mIERpYWJldGVzIGJ5IEh5cGVydGVuc2lvbiIsIHg9Ikh5cGVydGVuc2lvbiIsIHk9ICJGcmVxdWVuY3kiLAogICAgICAgIGZpbGw9ICJEaWFiZXRlcyIpCgpgYGAKCiMgQ29tcGFyaXNvbiBvZiBUd28gQ2F0ZWdvcmljYWwgVmFyaWFibGVzCgpUaGUgcHVycG9zZSBvZiB0aGlzIGdyYXBoIGlzIHRvIHNob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhhdmluZyBoeXBlcnRlbnNpb24gYW5kIGRpYWJldGVzLiBGb3IgdGhpcyBncmFwaCwgSSBjaGFuZ2VkIHRoZSB2YWx1ZXMgb2YgMSBpbiBib3RoIHRoZSBkaWFiZXRlcyBhbmQgaHlwZXJ0ZW5zaW9uIHZhcmlhYmxlIHRvICJZZXMiIGFuZCB0aGUgdmFsdWUgMCB0byAiTm8uIiBUaGVzZSBpbnRlZ2VycyB3ZXJlIG1lYW50IHRvIHJlcHJlc2VudCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2l0aCAxIGNvcnJlbGF0aW5nIHRvIGJlaW5nIHBvc2l0aXZlIGZvciBoeXBlcnRlbnNpb24gb3IgZGlhYmV0ZXMgYW5kIDAgY29ycmVsYXRpbmcgdG8gYmVpbmcgbmVnYXRpdmUgdG8gaHlwZXJ0ZW5zaW9uIG9yIGRpYWJldGVzLiBCYXNlZCBvbiBvdXIgZ3JhcGgsIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBubyBjb3JyZWxhdGlvbiBiZXR3ZWVuIGhhdmluZyBkaWFiZXRlcyBhbmQgaGF2aW5nIGh5cGVydGVuc2lvbiwgd2l0aCBtb3N0IHBhdGllbnRzIHdpdGhpbiBvdXIgc2FtcGxlIGhhdmluZyBuZWl0aGVyIGRpYWJldGVzIG9yIGh5cGVydGVuc2lvbi4gVGh1cywgaHlwZXJ0ZW5zaW9uIHdvdWxkIG5vdCBiZSBhIGdvb2QgcHJlZGljdG9yIG9mIGRpYWJldGVzIHdpdGhpbiBwYXRpZW50cy4KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgcGxvdCBzaG93cyB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiBibG9vZCBnbHVjb3NlIGxldmVscyBhbmQgSGJBMWMgbGV2ZWxzIG9uIGRpYWJldGVzLiBCYXNlZCBvbiB0aGUgcGxvdCwgaGlnaGVyIGJsb29kIGdsdWNvc2UgbGV2ZWxzIGFuZCBIYkExYyBsZXZlbHMgYXJlIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHRoZSBwcmVzZW5jZSBvZiBkaWFiZXRlcy4gSW4gY29tcGFyaXNvbiwgbG93ZXIgbGV2ZWxzLCBpbmRpY2F0ZSB0aGF0IGEgcGF0aWVudCB3aWxsIG1vcmUgY29tbW9ubHkgbm90IGhhdmUgZGlhYmV0ZXMuIFRoZSBoZWlnaHQgb2YgdGhlIHBvaW50cyBzaG93biBmb3IgZGlhYmV0ZXMgcmVwcmVzZW50IHRoZSBsZXZlbHMgb2YgSGJBMWMsIGJhc2VkIG9uIHRoZSBwbG90LCBIYkExYyBsZXZlbHMgcHJlZGljdCB0aGUgcHJlc2VuY2Ugb2YgZGlhYmV0ZXMgbW9yZSBvZnRlbiB0aGFuIGJsb29kIGdsdWNvc2UgbGV2ZWxzLiAnfQoKCmludGVyYWN0ID0gZ2dwbG90KGRhdGEsIGFlcyh4ID1ibG9vZF9nbHVjb3NlX2xldmVsICwgeSA9IEhiQTFjX2xldmVsLCBncm91cCA9IGRpYWJldGVzLCBjb2xvciA9IGRpYWJldGVzKSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKyAgIyBMaW5lcyByZXByZXNlbnRpbmcgaW50ZXJhY3Rpb24KICBnZW9tX3BvaW50KHNpemUgPSAyKSArICMgUG9pbnRzIGZvciBkYXRhCiAgbGFicygKICAgIHRpdGxlID0gIkludGVyYWN0aW9uIG9mIEJsb29kIEdsdWNvc2UgYW5kIEhiQTFjIExldmVscyIsCiAgICB4ID0gIkJsb29kIEdsdWNvc2UgTGV2ZWwiLAogICAgeSA9ICJIYkExYyBMZXZlbCIsCiAgICBjb2xvciA9ICJQcmVzZW5jZSBvZiBEaWFiZXRlcyIKICApICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIKICApCmludGVyYWN0CgpgYGAKIyBDb21wYXJpc29uIG9mIFR3byBOdW1lcmljYWwgVmFyaWFibGVzCgpUaGUgdHdvIHZhcmlhYmxlcyBiZWluZyBjb21wYXJlZCBoZXJlIGFyZSBCbG9vZCBHbHVjb3NlIExldmVsIGFuZCBIYkExYyBMZXZlbC4gSGJBMWNjIGlzIGEgc2hvcnRlbmVkIHRlcm0gZm9yIGhlbW9nbG9iaW4gQTFjLiBIYjFBYyBsZXZlbCBpcyBhIG1lYXN1cmUgb2YgYXZlcmFnZSBibG9vZCBzdWdhciBsZXZlbHMgb3ZlciAyLTMgbW9udGhzIGFuZCBpcyBtZWFzdXJlZCBpbiAlLiBCbG9vZCBnbHVjb3NlIGxldmVsIGlzIHRoZSBhbW91bnQgb2YgYmxvb2Qgc3VnYXIgaW4gYSBwYXRpZW50cyBibG9vZCBhdCBhIGdpdmVuIHRpbWUuIE91ciBwbG90IHNob3dzIGEgY29ycmVsYXRpb24gYmV0d2VlbiBoaWdoIGxldmVscyBpbiBIYkExYyBhbmQgYmxvb2QgZ2x1Y29zZSBsZXZlbHMgYW5kIGl0J3MgcmVsYXRpb25zaGlwIHdpdGggZGlhYmV0ZXMuIFRoaXMgcGxvdCBzdWdnZXN0cyBwYXRpZW50cyB3aXRoIGhpZ2ggbGV2ZWxzIG9mIEhiQTFjIGFuZCBibG9vZCBnbHVjb3NlIGxldmVscyBhcmUgYXQgaGlnaCByaXNrIGZvciBkaWFiZXRlcy4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIGJveHBsb3Qgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFnZSBhbmQgcHJlc2VuY2Ugb2YgZGlhYmV0ZXMuIFRoZSBib3hwbG90IHNob3dzIHRoYXQgb2xkZXIgcGF0aWVudHMgb24gYXZlcmFnZSBhcmUgbW9yZSBvZnRlbiBoYXZlIGRpYWJldGVzLiBUaGVyZSBhcmUgYSBmZXcgb3V0bGllcnMgaW4gdGhlIGRpYWJldGVzIHBvcHVsYXRpb24gdGhhdCBhcHBlYXIgdG8gYmUgdW5kZXIgdGhlIGFnZSBvZiAyMCB5ZWFycy4gVGhpcyBjb3VsZCBiZSByZXByZXNlbnRhdGl2ZSBvZiBwYXRpZW50cyB3aXRoIFR5cGUgSSBkaWFiZXRlcywgc2luY2UgdGhlIGRhdGEgZG9lcyBub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiBUeXBlIEkgYW5kIFR5cGUgSUkgZGlhYmV0ZXMuICd9CgojcGxvdCBvZiBhIG51bWVyaWNhbCBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGUKIGJveHBsb3QoZGF0YSRhZ2UgfiBkYXRhJGRpYWJldGVzLAogICAgICAgICB4bGFiPSJQYXRpZW50IEFnZSIsCiAgICAgICAgIHlsYWI9IlByZXNlbmNlIG9mIERpYWJldGVzIiwKICAgICAgICAgY29sID0gYygic2t5Ymx1ZSIsICJwdXJwbGUiKSwKICAgICAgICAgbWFpbj0iVmlzdWFsaXphdGlvbiBvZiBEaWFiZXRlcyBieSBBZ2UiLAogICAgICAgICBjZXgubWFpbiA9IDEuMSwKICAgICAgICAgY29sLm1haW4gPSAibmF2eSIpCmBgYAoKIyBDb21wYXJpc29uIG9mIGEgTnVtZXJpY2FsIGFuZCBDYXRlZ29yaWNhbCBWYXJpYWJsZXMKClRoaXMgY2hhcnQgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFnZSBhbmQgZGlhYmV0ZXMgZGlhZ25vc2lzLiBUaGlzIGNoYXJ0IHNob3dzIHRoYXQgYXMgcGF0aWVudHMgZ3JvdyBvbGRlciB0aGV5IGFyZSBhdCBoaWdoZXIgcmlzayBmb3IgZGlhYmV0ZXMuIFRoaXMgd291bGQgbWFrZSBhZ2UgYSBwcmVkaWN0b3IgZm9yIGRpYWJldGVzLCB3aXRoIHRoZSBhdmVyYWdlIGFnZSBmb3IgdGhvc2Ugd2l0aG91dCBkaWFiZXRlcyBiZWluZyA0MCB5ZWFycyBhbmQgNjAgeWVhcnMgZm9yIHRob3NlIHdpdGggZGlhYmV0ZXMuIEhvd2V2ZXIsIHdlIGNhbiBzZWUgdGhhdCB3ZSBkbyBoYXZlIGEgZmV3IG91dGxpZXJzIGZvciB0aG9zZSB3aXRoIGRpYWJldGVzIHdpdGggc29tZSBwYXRpZW50cyBiZWluZyBkaWFnbm9zZWQgYXQgMjAgb3IgeW91bmdlci4gVGhpcyBjb3VsZCBiZSBmcm9tIHRoZSBwb3B1bGF0aW9uIG9mIHBlb3BsZSB3aXRoIFR5cGUgSSBkaWFiZXRlcyB3aGljaCBpcyB1c3VhbGx5IGRpYWdub3NlZCBpbiBwYXRpZW50cyB1bmRlciAyMC4gT3VyIGRhdGFzZXQgZG9lcyBub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0eXBlIEkgYW5kIHR5cGUgSUkgZGlhYmV0ZXMuCgoKIyBDb25jbHVzaW9uIGZvciBDb21wYXJpc29uIG9mIFZhcmlhYmxlcwoKRGlhYmV0ZXMgY2FuIGJlIHByZWRpY3RlZCB1c2luZyBhZ2UsIGJsb29kIGdsdWNvc2UgbGV2ZWxzLCBhbmQgSGJBMWMgbGV2ZWxzIGluIHBhdGllbnRzLiBIb3dldmVyLCBvdXIgcmVzdWx0cyBzaG93ZWQgaHlwZXJ0ZW5zaW9uIGlzIG5vdCBhIGdvb2QgcHJlZGljdG9yIG9mIGRpYWJldGVzLiBCYXNlZCBvbiBvdXIgZmluZGluZ3MsIGl0IHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIGxvb2sgYXQgZmFjdG9ycyB0aGF0IG9jY3VyIG9uY2UgYSBwYXRpZW50IGJlY29tZXMgb2xkZXIgdGhhdCBjb3VsZCBsZWFkIHRvIGRpYWJldGVzIGF0IGFuIG9sZGVyIGFnZS4gQXMgd2VsbCwgZGlldCBjb3VsZCBiZSBhbm90aGVyIGZhY3RvciB0byBsb29rIGludG8sIHN1Y2ggYXMgZ3JhbXMgb2Ygc3VnYXIgY29uc3VtZWQgb24gYSBkYWlseSBvciB3ZWVrbHkgYmFzaXMsIHRvIHNlZSBpZiBzdWdhciBjb25zdW1wdGlvbiBjYW4gcHJlZGljdCBkaWFiZXRlcy4gQW5vdGhlciBpbXBvcnRhbnQgdGhpbmcgdG8gYW5hbHl6ZSBpbiBmdXR1cmUgc3R1ZGllcyB3b3VsZCBiZSBUeXBlIEkgdmVyc3VzIFR5cGUgSUkgZGlhYmV0ZXMsIGFzIGl0IGNvdWxkIGJlIHRoZSByZWFzb24gZm9yIHNvbWUgb2YgdGhlIG91dGxpZXJzIGluIHRoZSBib3hwbG90LiAKCgojIyBPdmVydmlldyBvZiBNaXNzaW5nIFZhcmlhYmxlcwoKQW4gaW1wb3J0YW50IHN0ZXAgaW4gZGF0YSBhbmFseXNpcyBmb3IgbWFjaGluZSBsZWFybmluZyBpcyBoYW5kbGluZyBtaXNzaW5nIHZhbHVlcy4gTWlzc2luZyB2YWx1ZXMgaW4gYSBkYXRhc2V0IGNhbiBvY2N1ciBmb3IgbWFueSByZWFzb25zLCBidXQgbm90IGxpbWl0ZWQgdG8gbm9uLXJlc3BvbnNlIGluIHN1cnZleXMsIHBhcnRpY2lwYW50cyBsZWF2aW5nIHRoZSBzdHVkeSwgZGF0YSBlbnRyeSBlcnJvcnMgYW5kIHN5c3RlbSBsaW1pdGF0aW9ucy4gSWYgbWlzc2luZyB2YWx1ZXMgYXJlIG5vdCBhZGRyZXNzZWQsIG1vZGVscyBjYW4gYmVjb21lIGJpYXNlZCBhbmQgYWNjdXJhY3kgb2YgdGhlIGFuYXlseXNpcyBjYW4gZGVjcmVhc2UuIFRoaXMgc2VjdGlvbiBleGFtaW5lcyBkaWZmZXJlbnQgaW1wdXRhdGlvbiBzdHJhdGVnaWVzIHRvIGVmZmVjdGl2ZWx5IGhhbmRsZSBtaXNzaW5nIHZhbHVlcy4KClRoZSBpbXB1dGF0aW9uIG1ldGhvZHMgd2Ugd2lsbCBleGFtaW5lIGluY2x1ZGU6CgpSZXBsYWNlbWVudCBJbXB1dGF0aW9uIGZvciBDYXRlZ29yaWNhbCBGZWF0dXJlczogTW9kZSBpbXB1dGF0aW9uIGFuZCBLTk4tYmFzZWQgaW1wdXRhdGlvbi4KClJlZ3Jlc3Npb24tYmFzZWQgSW1wdXRhdGlvbiBmb3IgTnVtZXJpY2FsIEZlYXR1cmVzOiBQcmVkaWN0aXZlIG1vZGVsaW5nIHRvIGVzdGltYXRlIG1pc3NpbmcgdmFsdWVzLgoKTXVsdGlwbGUgSW1wdXRhdGlvbjogQWR2YW5jZWQgbWV0aG9kcyBzdWNoIGFzIE1JQ0UgdG8gaW1wcm92ZSByb2J1c3RuZXNzLgoKYGBge3J9CiMgY3JlYXRlIHJhbmRvbSBvYnNlcnZhdGlvbiBJRCBhbmQgcmVwbGFjZSB0aGUgY29ycmVzcG9uZGluZyBvYnMgd2l0aCBtaXNzaW5nCmx0ci5taXNzaW5nLmlkIDwtIHNhbXBsZSgxOjEwMDAsIDUwLCByZXBsYWNlID0gRkFMU0UpCmdwYS5taXNzaW5nLmlkIDwtIHNhbXBsZSgxOjEwMDAsIDE1LCByZXBsYWNlID0gRkFMU0UpIApkaWFiZXRlc2QkYm1pW2x0ci5taXNzaW5nLmlkXSA8LSBOQQpkaWFiZXRlc2QkaGVhcnRfZGlzZWFzZVtncGEubWlzc2luZy5pZF0gPC0gTkEKZGlhYmV0ZXNkJHNtb2tpbmdfaGlzdG9yeVtkaWFiZXRlc2Qkc21va2luZ19oaXN0b3J5ID09ICJObyBJbmZvIl0gPC0gTkEKCmBgYAoKIyBNb2RlIEltcHV0YXRpb24gZm9yIENhdGVnb3JpY2FsIFZhcmlhYmxlcwoKSW4gdGhpcyBtZXRob2QsIG1pc3NpbmcgdmFyaWFibGVzIGFyZSByZXBsYWNlZCB3aXRoIHRoZSBtb3N0IGZyZXF1ZW50IGNhdGVnb3J5IG9mIHRoZSBjb3JyZXNwb25kaW5nIHZhcmlhYmxlLiBNb2RlIGltcHV0YXRpb24gZW5zdXJlcyBjb25zaXN0ZW5jeSBhbmQgd29ya3Mgd2VsbCB3aGVuIG1pc3NpbmcgdmFsdWVzIGFyZSByYW5kb21seSBkaXN0cmlidXRlZC4gQmVsb3csIG1vZGUgaW1wdXRhdGlvbiBpcyB1c2VkIG9uIHRoZSB2YXJpYWJsZSwgaGVhcnQgZGlzZWFzZSwgd2hpY2ggaXMgMSBpZiB0aGUgcGF0aWVudCBoYXMgaGVhcnQgZGlzZWFzZSBhbmQgMCBpZiB0aGUgcGF0aWVudCBkb2VzIG5vdCBoYXZlIGhlYXJ0IGRpc2Vhc2UuVGhlIHZhcmlhYmxlIHNtb2tpbmcgaGlzdG9yeSBoYWQgY3VycmVudCwgZm9ybWVyLCBubyBpbmZvLCBuZXZlciwgYW5kIGV2ZXIgYXMgcG9zc2libGUgdmFsdWVzLiBObyBJbmZvIGluZGljYXRlcyB0aGF0IHdlIGRvIG5vdCBoYXZlIGluZm9ybWF0aW9uIG9uIHRoZSBwYXJ0aWNpcGFudCdzIHNtb2tpbmcgaGlzdG9yeSBhbmQgd2lsbCBiZSB0cmVhdGVkIGFzIGEgbWlzc2luZyB2YWx1ZXMuCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9J1RoZSBwbG90IHNob3dzIHRoZSBjb21wYXJpc29uIGJldHdlZW4gdGhlIG9yaWdpbmFsIGRhdGEgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlIHZhcmlhYmxlcyBoZWFydCBkaXNlYXNlIGFuZCBzbW9raW5nIGhpc3RvcnksIGFuZCB0aGUgZGF0YSBhZnRlciB1c2luZyBtb2RlIGltcHV0ZS4gVGhlIGhpc3RvZ3JhbXMgc2hvdyB0aGF0IG1vZGUgaW1wdXRlIHVzZWQgd2hhdCB3YXMgbW9zdCBjb21tb24gdG8gcmVwbGFjZSB0aGUgbWlzc2luZyB2YWx1ZXMuIFRoaXMgaXMgc2VlbiBjbGVhcmx5IGluIHRoZSB2YXJpYWJsZSBzbW9raW5nIGhpc3RvcnksIHdoZXJlICJuZXZlciIgd2VudCBmcm9tIG9yaWdpbmFsbHkgYSBjb3VudCBvZiBhYm91dCA0MCwwMDAgdG8gYWJvdXQgNzAsMDAwLiBIZWFydCBkaXNlYXNlIGhhZCBhIHNtYWxsIGFtb3VudCBvZiBtaXNzaW5nIGRhdGEgKG9ubHkgMTUpLCBob3dldmVyIHRoZSAxNSBtaXNzaW5nIHdlcmUgY2hhbmdlZCB0byAwLCBpbmRpY2F0aW5nIHRoZSBwYXRpZW50IHdvdWxkIG5vdCBoYXZlIGhlYXJ0IGRpc2Vhc2UuJ30KCgpkaWFiZXRlc2QgPC1kaWFiZXRlc2QKIyBGdW5jdGlvbiB0byBpbXB1dGUgbW9kZQoKTW9kZSA8LSBmdW5jdGlvbih4KSB7CiAgdXggPC0gdW5pcXVlKG5hLm9taXQoeCkpICAjIFJlbW92ZSBOQXMgYmVmb3JlIGNvbXB1dGluZyBtb2RlCiAgdGFiIDwtIHRhYnVsYXRlKG1hdGNoKHgsIHV4KSkKICBtb2RlX3ZhbHVlIDwtIHV4W3doaWNoLm1heCh0YWIpXQogIAogICMgRW5zdXJlIG1vZGVfdmFsdWUgaXMgb2YgdGhlIHNhbWUgdHlwZSBhcyB4CiAgaWYgKGlzLmZhY3Rvcih4KSkgewogICAgcmV0dXJuKGZhY3Rvcihtb2RlX3ZhbHVlLCBsZXZlbHMgPSBsZXZlbHMoeCkpKQogIH0gZWxzZSBpZiAoaXMuY2hhcmFjdGVyKHgpKSB7CiAgICByZXR1cm4oYXMuY2hhcmFjdGVyKG1vZGVfdmFsdWUpKQogIH0gZWxzZSB7CiAgICByZXR1cm4oYXMubnVtZXJpYyhtb2RlX3ZhbHVlKSkKICB9Cn0KI2FwcGx5IG1vZGUgaW1wdXRhdGlvbgp2YWx1ZV9pbXB1dGVkIDwtIGRhdGEuZnJhbWUoCiAgb3JpZ2luYWwgPSBkaWFiZXRlc2QkaGVhcnRfZGlzZWFzZSwKICBvcmlnaW5hbDI9ZGlhYmV0ZXNkJHNtb2tpbmdfaGlzdG9yeSwKICBpbXB1dGVkX21vZGVoZCA9IHJlcGxhY2UoZGlhYmV0ZXNkJGhlYXJ0X2Rpc2Vhc2UsIGlzLm5hKGRpYWJldGVzZCRoZWFydF9kaXNlYXNlKSwgTW9kZSgoZGlhYmV0ZXNkJGhlYXJ0X2Rpc2Vhc2UpKSksCiAgaW1wdXRlZF9tb2Rlc2ggPSByZXBsYWNlKGRpYWJldGVzZCRzbW9raW5nX2hpc3RvcnksIGlzLm5hKGRpYWJldGVzZCRzbW9raW5nX2hpc3RvcnkpLCBNb2RlKGRpYWJldGVzZCRzbW9raW5nX2hpc3RvcnkpKSkKc3VtbWFyeSh2YWx1ZV9pbXB1dGVkKQoKI3Bsb3QgdGhlIGNvbXBhcmlzb24gb2Ygb3JpZ2luYWwgZGF0YSBhbmQgbW9kZSBpbXB1dGVkIGRhdGEKaDEgPC0gZ2dwbG90KHZhbHVlX2ltcHV0ZWQsIGFlcyh4ID0gb3JpZ2luYWwpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz0xMCwgZmlsbCA9ICIjYWQxNTM4IiwgY29sb3IgPSAiIzAwMDAwMCIsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKwogIGdndGl0bGUoIk9yaWdpbmFsIGZvciBIZWFydCBEaXNlYXNlIikgKwogIHRoZW1lX2NsYXNzaWMoKQpoMiA8LSBnZ3Bsb3QodmFsdWVfaW1wdXRlZCwgYWVzKHggPSBvcmlnaW5hbDIpKSArCiAgZ2VvbV9iYXIoIGZpbGwgPSAiIzE1YWQ0ZiIsIGNvbG9yID0gIiMwMDAwMDAiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICBnZ3RpdGxlKCJPcmlnaW5hbCBmb3IgU21va2luZyBIaXN0b3J5IikgKwogIHRoZW1lX2NsYXNzaWMoKQpoMyA8LSBnZ3Bsb3QodmFsdWVfaW1wdXRlZCwgYWVzKHggPSBpbXB1dGVkX21vZGVoZCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zPTEwLCBmaWxsID0gIiMxNTQzYWQiLCBjb2xvciA9ICIjMDAwMDAwIiwgcG9zaXRpb24gPSAiaWRlbnRpdHkiKSArCiAgZ2d0aXRsZSgiTW9kZS1pbXB1dGVkIGZvciBIZWFydCBEaXNlYXNlIikgKwogIHRoZW1lX2NsYXNzaWMoKQpoNCA8LSBnZ3Bsb3QodmFsdWVfaW1wdXRlZCwgYWVzKHggPSBpbXB1dGVkX21vZGVzaCkpICsKICBnZW9tX2JhciggZmlsbCA9ICIjYWQ4NDE1IiwgY29sb3IgPSAiIzAwMDAwMCIsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKwogIGdndGl0bGUoIk1vZGUtaW1wdXRlZCBmb3IgU21va2luZyBIaXN0b3J5IikgKwogIHRoZW1lX2NsYXNzaWMoKQoKCnBsb3RfZ3JpZChoMSwgaDIsIGgzLCBoNCwgbnJvdyA9IDIsIG5jb2wgPSAyKQoKCgpgYGAKIyBSZWdyZXNzaW9uLUJhc2VkIEltcHV0YXRpb24gZm9yIE51bWVyaWNhbCBWYXJpYWJsZXMKClVzaW5nIHJlZ3Jlc3Npb24gbW9kZWxzLCB3ZSBjYW4gZXN0aW1hdGUgbWlzc2luZyB2YWx1ZXMuIEZvciB0aGUgdmFyaWFibGUsIGJtaSwgd2UgY2FuIHByZWRpY3QgdGhlIG1pc3NpbmcgdmFsdWVzIHVzaW5nIGVzdGltYXRlcyBmcm9tIG91ciBtb2RlbC4gQm1pIGlzIGEgY29udGludW91cyB2YXJpYWJsZSwgdW5saWtlIHRoZSBwcmV2aW91cyBleGFtcGxlIHdoZXJlIG91ciB2YWx1ZXMgY291bGQgb25seSBiZSAwIG9yIDEuIEluIHRoaXMgbWV0aG9kLCBhIGxpbmVhciBtb2RlbCB3YXMgY3JlYXRlZCB0byBoZWxwIGNyZWF0ZSBlc3RpbWF0ZXMgb2Ygb3VyIHZhbHVlcy4gCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9J1RoZSBib3hwbG90cyBzaG93IHRoZSBjb21wYXJpc29uIG9mIHRoZSB2YXJpYWJsZSBibWkgYmVmb3JlIGFuZCBhZnRlciByZWdyZXNzaW9uIGltcHV0YXRpb24uJyB9CgojY3JlYXRlIGEgZGF0YXNldCBmb3IgcmVncmVzc2lvbiBpbXB1dGUKcmVnaW1wdXRlPC1kaWFiZXRlc2QKCiMgSWRlbnRpZnkgcm93cyB3aGVyZSBCTUkgaXMgbWlzc2luZwptaXNzaW5nX3Jvd3MgPC0gd2hpY2goaXMubmEoZGlhYmV0ZXNkJGJtaSkpICAjIEdldCByb3cgaW5kaWNlcwoKCiMgVHJhaW4gYSBsaW5lYXIgbW9kZWwgdXNpbmcgY29tcGxldGUgY2FzZXMKbG1fbW9kZWwgPC0gbG0oYm1pIH4gYWdlICsgYmxvb2RfZ2x1Y29zZV9sZXZlbCArIEhiQTFjX2xldmVsICsgaGVhcnRfZGlzZWFzZSArIGh5cGVydGVuc2lvbiArIGRpYWJldGVzLCAKICAgICAgICAgICAgICAgZGF0YSA9IHJlZ2ltcHV0ZSwgbmEuYWN0aW9uID0gbmEuZXhjbHVkZSkKCiMgSW1wdXRlIG1pc3NpbmcgQk1JIHZhbHVlcyB1c2luZyB0aGUgbW9kZWwKZGlhYmV0ZXNkJGJtaVttaXNzaW5nX3Jvd3NdIDwtIHByZWRpY3QobG1fbW9kZWwsIG5ld2RhdGEgPSByZWdpbXB1dGVbbWlzc2luZ19yb3dzLCBdKQoKCmRlcC5taSA8LSBiaW5kX3Jvd3MoCiAgZGF0YS5mcmFtZSh2YWx1ZSA9IGRpYWJldGVzZCRibWksIGltcHV0ZWQgPSByZWdpbXB1dGUkYm1pKSkKICAgICAgICAgICAgICNpbXAgPSAiZGVwLmltcDEiKQoKCmk3PC1ib3hwbG90KGRpYWJldGVzZCRibWksCm1haW4gPSAiTWVhbiBibWkgaW4gT3JpZ2luYWwgRGF0YSIsCnhsYWIgPSAiQk1JIiwKY29sID0gImJsdWUiLApib3JkZXIgPSAiYmxhY2siLApob3Jpem9udGFsID0gVFJVRSwKbm90Y2ggPSBUUlVFCikKaTggPC1ib3hwbG90KHJlZ2ltcHV0ZSRibWksCm1haW4gPSAiUmVncmVzc2lvbiBJbXB1dGF0aW9uIGZvciBCTUkiLAp4bGFiID0gIkJNSSIsCmNvbCA9ICJyZWQiLApib3JkZXIgPSAiYmxhY2siLApob3Jpem9udGFsID0gVFJVRSwKbm90Y2ggPSBUUlVFCikKCgogCgpgYGAKCiMgTUlDRSBJbXB1dGF0aW9uCgpNSUNFIGlzIG11bHRpcGxlIGltcHV0YXRpb24gdGVjaG5pcXVlcyB1c2VkIHRvIGltcHV0ZSBtaXNzaW5nIHZhbHVlcy4gVGhpcyBtZXRob2QgYWxzbyB1c2VzIHJlZ3Jlc3Npb24gbW9kZWxzIGFuZCBhY2NvdW50cyBmb3IgdW5jZXJ0YWludHkgYnkgZ2VuZXJhdGluZyBtdWx0aXBsZSBwb3NzaWJsZSB2YWx1ZXMuIFVzaW5nIE1JQ0UsIHRoZSB2YXJpYWJsZXMgc21va2luZyBoaXN0b3J5LCBoZWFydCBkaXNlYXNlLCBhbmQgYm1pLCBjYW4gYmUgaW1wdXRhdGVkIHdpdGhpbiBvbmUgZnVuY3Rpb24uIE1JQ0UgY2FuIGhhbmRsZSBkaWZmZXJlbnQgdHlwZXMgb2YgdmFyaWFibGVzLCBpbXB1dGF0ZXMgYmFzZWQgb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhYmxlcyBhbmQgaXQgYWNjb3VudHMgZm9yIHZhcmlhYmlsaXR5IGluIG1pc3NpbmcgZGF0YS4gCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nQmFzZWQgb24gdGhlIHRocmVlIGdyYXBocywgTUlDRSBzZWVtcyB0byBoYXZlIHByb2R1Y2VkIHZhbHVlcyB0aGF0IGFwcGVhciBhbG1vc3QgaWRlbnRpY2FsIHRvIHRoZSBvcmlnaW5hbCBkYXRhc2V0LiBNb2RlIGltcHV0ZSBzZWVtcyB0byBiZSBzbGlnaHRseSBkaWZmZXJlbnQgc2luY2UgaXQgcmVsaWVzIG9uIGZpbGxpbmcgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgbW9zdCBjb21tb24gdmFsdWUuIEluIHRoaXMgY2FzZSwgbW9kZSBpbXB1dGUgZmlsbGVkIGFsbCBtaXNzaW5nIHZhbHVlcyBhcyAwIGFuZCB1bmRlci1yZXByZXNlbnRlZCAxLid9CgojcmVsb2FkIGRhdGFzZXQgc28gdGhhdCBpdCBpcyBmcmVlIG9mIG1vZGUgYW5kIHJlZ3Jlc3Npb24gaW1wdXRhdGlvbiBkb25lIHByZXZpb3VzbHkKZGF0YTIgPC0gcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9HYWJieUs4L0RhdGFzZXRzL3JlZnMvaGVhZHMvbWFpbi9kaWFiZXRlc19wcmVkaWN0aW9uX2RhdGFzZXQuY3N2JykKCiNjcmVhdGUgbWlzc2luZyB2YWx1ZXMgYW5kIG1ha2Ugc21va2luZyBoaXN0b3J5IGEgYmluYXJ5IHZhcmlhYmxlCmx0ci5taXNzaW5nLmlkIDwtIHNhbXBsZSgxOjEwMDAsIDUwLCByZXBsYWNlID0gRkFMU0UpCmdwYS5taXNzaW5nLmlkIDwtIHNhbXBsZSgxOjEwMDAsIDE1LCByZXBsYWNlID0gRkFMU0UpIApkYXRhMiRzbW9raW5nX2hpc3RvcnlbZGF0YTIkc21va2luZ19oaXN0b3J5PT0gIk5vIEluZm8iXSA8LU5BCmRhdGEyJGJtaVtsdHIubWlzc2luZy5pZF0gPC0gTkEKZGF0YTIkaGVhcnRfZGlzZWFzZVtncGEubWlzc2luZy5pZF0gPC0gTkEKCiNtYWtlIHZhcmlhYmxlcyBudW1lcmljCmRhdGEyJGdlbmRlciA8LSBhcy5udW1lcmljKGFzLmZhY3RvcihkYXRhMiRnZW5kZXIpKQpkYXRhMiRoZWFydF9kaXNlYXNlPC0gYXMuZmFjdG9yKGFzLm51bWVyaWMoZGF0YTIkaGVhcnRfZGlzZWFzZSkpCmRhdGEyJHNtb2tpbmdfaGlzdG9yeTwtYXMuZmFjdG9yKGRhdGEyJHNtb2tpbmdfaGlzdG9yeSkKI2FwcGx5IE1JQ0UgaW1wdXRhdGlvbgpkZl9taWNlIDwtIG1pY2UoZGF0YTIsIG1ldGhvZCA9IGMoIiIsIiIsIiIsInBtbSIsInBvbHlyZWciLCJwbW0iLCIiLCIiLCIiKSwgIG0gPSAyMCwgbWF4aXQ9MjAsc2VlZCA9IDEyMywgcHJpbnQ9RikKCiMgQ29tcGxldGUgZGF0YXNldCB3aXRoIGltcHV0ZWQgdmFsdWVzCmRmX2ltcHV0ZWQgPC0gY29tcGxldGUoZGZfbWljZSkKCiNwbG90IGNvbXBhcmlzb25zIG9mIGJlZm9yZSBhbmQgYWZ0ZXIgTUlDRSBpbXB1dGF0aW9uIGZvciBoZWFydCBkaXNlYXNlCmcxIDwtIGdncGxvdCh2YWx1ZV9pbXB1dGVkLCBhZXMoeCA9IGltcHV0ZWRfbW9kZWhkKSkgKwogIGdlb21fYmFyKGZpbGwgPSAiIzE1NDNhZCIsIGNvbG9yID0gIiMwMDAwMDAiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICBnZ3RpdGxlKCJNb2RlLWltcHV0ZWQiKSArCiAgbGFicyh4PSJIZWFydCBEaXNlYXNlIikrCiAgdGhlbWVfY2xhc3NpYygpCmcyIDwtIGdncGxvdCh2YWx1ZV9pbXB1dGVkLCBhZXMoeCA9IG9yaWdpbmFsKSkgKwogIGdlb21fYmFyKGZpbGwgPSAiI2FkMTUzOCIsIGNvbG9yID0gIiMwMDAwMDAiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICBnZ3RpdGxlKCJPcmlnaW5hbCIpICsKICBsYWJzKHg9IkhlYXJ0IERpc2Vhc2UiKSsKICB0aGVtZV9jbGFzc2ljKCkKZzMgPC0gZ2dwbG90KGRmX2ltcHV0ZWQsIGFlcyh4ID0gaGVhcnRfZGlzZWFzZSkpICsKICBnZW9tX2JhcihmaWxsID0gInB1cnBsZSIsIGNvbG9yID0gIiMwMDAwMDAiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICBnZ3RpdGxlKCJNSUNFIikgKwogIGxhYnMoeD0iSGVhcnQgRGlzZWFzZSIpKwogIHRoZW1lX2NsYXNzaWMoKQpwbG90X2dyaWQoZzEsIGcyLCBnMywgbnJvdz0xLCBuY29sID0zKQpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIGJhciBncmFwaHMgc2hvdyB0aGUgY29tcGFyaXNvbiBvZiB0aGUgb3JpZ2luYWwgZGF0YSB0byBtb2RlIGltcHV0YXRpb24gYW5kIE1JQ0UuIE1JQ0UgYXBwZWFycyB0byBoYXZlIGRpc3BlcnNlZCB0aGUgZGF0YSBtb3JlIGV2ZW5seSBhY3Jvc3MgdGhlIGRpZmZlcmVudCBjYXRlZ29yaWVzLCB3aGlsZSBtb2RlIGltcHV0YXRpb24gaGFzIG9ubHkgYWRkZWQgdmFsdWVzIHRvICJuZXZlci4nfQojcGxvdCBjb21wYXJpc29ucyBmb3Igc21va2luZyBoaXN0b3J5Cmc0IDwtIGdncGxvdChkZl9pbXB1dGVkLCBhZXMoeCA9IHNtb2tpbmdfaGlzdG9yeSkpICsKICBnZW9tX2JhciggZmlsbCA9ICJwdXJwbGUiLCBjb2xvciA9ICIjMDAwMDAwIiwgcG9zaXRpb24gPSAiaWRlbnRpdHkiKSArCiAgZ2d0aXRsZSgiTUlDRSIpICsKICBsYWJzKHg9IlNtb2tpbmcgSGlzdG9yeSIpKwogIHRoZW1lX2NsYXNzaWMoKQpnNSA8LSBnZ3Bsb3QodmFsdWVfaW1wdXRlZCwgYWVzKHggPSBvcmlnaW5hbDIpKSArCiAgZ2VvbV9iYXIoIGZpbGwgPSAicmVkIiwgY29sb3IgPSAiIzAwMDAwMCIsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKwogIGdndGl0bGUoIk9yaWdpbmFsIFNtb2tpbmcgSGlzdG9yeSIpICsKICBsYWJzKHg9IlNtb2tpbmcgSGlzdG9yeSIpKwogIHRoZW1lX2NsYXNzaWMoKQpnNiA8LSBnZ3Bsb3QodmFsdWVfaW1wdXRlZCwgYWVzKHggPSBpbXB1dGVkX21vZGVzaCkpICsKICBnZW9tX2JhciggZmlsbCA9ICJibHVlIiwgY29sb3IgPSAiIzAwMDAwMCIsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKwogIGdndGl0bGUoIk1vZGUtaW1wdXRlZCIpICsKICBsYWJzKHg9IlNtb2tpbmcgSGlzdG9yeSIpKwogIHRoZW1lX2NsYXNzaWMoKQpwbG90X2dyaWQoZzQsZzUsZzYsIG5yb3c9MywgbmNvbD0xKQpgYGAKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgYm94cGxvdHMgc2hvdyB0aGUgY29tcGFyaXNvbiBvZiB0aGUgb3JpZ2luYWwgZGF0YSB0byBib3RoIE1JQ0UgYW5kIHJlZ3Jlc3Npb24gaW1wdXRhdGlvbi4nfQoKI3Bsb3QgY29tcGFyaXNvbnMgZm9yIGJtaQpnNzwtYm94cGxvdChkaWFiZXRlc2QkYm1pLAptYWluID0gIk1lYW4gYm1pIGluIE9yaWdpbmFsIERhdGEiLAp4bGFiID0gIkJNSSIsCmNvbCA9ICJibHVlIiwKYm9yZGVyID0gImJsYWNrIiwKaG9yaXpvbnRhbCA9IFRSVUUsCm5vdGNoID0gVFJVRQopCmc4IDwtYm94cGxvdChyZWdpbXB1dGUkYm1pLAptYWluID0gIlJlZ3Jlc3Npb24gSW1wdXRhdGlvbiBmb3IgQk1JIiwKeGxhYiA9ICJCTUkiLApjb2wgPSAicmVkIiwKYm9yZGVyID0gImJsYWNrIiwKaG9yaXpvbnRhbCA9IFRSVUUsCm5vdGNoID0gVFJVRQopCmc5IDwtYm94cGxvdChkZl9pbXB1dGVkJGJtaSwKbWFpbiA9ICJNSUNFIGZvciBCTUkiLAp4bGFiID0gIkJNSSIsCmNvbCA9ICJwdXJwbGUiLApib3JkZXIgPSAiYmxhY2siLApob3Jpem9udGFsID0gVFJVRSwKbm90Y2ggPSBUUlVFCikKCgoKYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9Cm1vZGVsNSA8LSB3aXRoKGRmX21pY2UsIGxtKGRpYWJldGVzIH4gYm1pKyBoZWFydF9kaXNlYXNlKyBzbW9raW5nX2hpc3RvcnkgKSkKc3VtbWFyeShwb29sKG1vZGVsNSkpCmBgYAojIFNrZXduZXNzCgpTa2V3bmVzcyBpcyBhIG1lYXN1cmUgb2Ygc3ltbWV0cnkgd2l0aGluIGEgZGlzdHJpYnV0aW9uLiBUaGUgc2tld25lc3MgbWVhc3VyZW1lbnRzIGJlbG93IGFyZSBvZiB0aGUgdmFyaWFibGVzIGFnZSwgYm1pLCBIYkExYyBsZXZlbCwgYW5kIGJsb29kIGdsdWNvc2UgbGV2ZWwsIHJlc3BlY3RpdmVseS4gV2hlbiBza2V3bmVzcyBpcyBlcXVhbCB0byAwLCB3ZSBjYW4gYXNzdW1lIHRoZSBkYXRhIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBXaGVuIHNrZXduZXNzIGlzIGdyZWF0ZXIgdGhhbiAwLCBvdXIgZGF0YSBpcyBwb3NpdGl2ZWx5IHNrZXdlZCwgd2hpbGUgYSB2YWx1ZSBsZXNzIHRoYW4gemVybyBpbmRpY2F0ZXMgbmVnYXRpdmVseSBza2V3ZWQgZGF0YS4gRm9yIHRoZSBkaWFiZXRlcyBkYXRhc2V0LCB0aGUgdmFyaWFibGVzIGJtaSBhbmQgYmxvb2QgZ2x1Y29zZSBsZXZlbCBhcmUgMSBvciBhYm91dCAxLCB3aGljaCBpbmRpY2F0ZSBhIHNpZ25pZmljYW50IHJpZ2h0IHNrZXcgaW4gdGhlIGRpc3RyaWJ1dGlvbi4gCmBgYHtyfQoKCgphZ2UxPC0gc2tld25lc3MoZGZfaW1wdXRlZCRhZ2UpIApibWkxPC1za2V3bmVzcyhkZl9pbXB1dGVkJGJtaSkgCkhiQTFjMTwtc2tld25lc3MoIGRmX2ltcHV0ZWQkSGJBMWNfbGV2ZWwpIApnbHVjb3NlMTwtc2tld25lc3MoZGZfaW1wdXRlZCRibG9vZF9nbHVjb3NlX2xldmVsKQoKbXl0YWJsZTwtZGF0YS5mcmFtZShhZ2UxLGJtaTEsSGJBMWMxLCBnbHVjb3NlMSkKa2FibGUobXl0YWJsZSwgY2FwdGlvbj0gIlNrZXduZXNzIG9mIE51bWVyaWMgVmFyaWFibGVzIikKCmBgYAojIFRyYW5zZm9ybWluZyBEYXRhCgpUbyBoYW5kbGUgb3VyIHNrZXdlZCB2YXJpYWJsZXMsIHdlIGNhbiBub3JtYWxpemUgdGhlIHZhcmlhYmxlcyBibWkgYW5kIGJsb29kIGdsdWNvc2UgbGV2ZWwuIE5vcm1hbGl6YXRpb24gcmVzY2FsZXMgZGF0YSBpbnRvIGEgZml4ZWQgcmFuZ2Ugb2YgWzAsMV0uIEJ5IG5vcm1hbGl6aW5nIG91ciBkYXRhLCB3ZSBjYW4gZml4IHNrZXduZXNzIGFuZCBjcmVhdGUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgZGF0YS4gVGhlIHZhcmlhYmxlcywgYWdlIGFuZCBIYkExYyBsZXZlbCBoYXZlIGJlZW4gc3RhbmRhcmRpemVkIHRvIGtlZXAgdGhlIGRpc3RyaWJ1dGlvbiBjZW50ZXJlZC4gU3RhbmRhcmRpemF0aW9uIGlzIHVzZWQgdG8gY3JlYXRlIGEgbWVhbiBvZiAwIGFuZCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiAxLCB0byBmb2xsb3cgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBCYXNlZCBvbiB0aGUgcmVzdWx0cyBiZWxvdywgd2UgY2FuIHNlZSB0aGF0IHRoZSB0cmFuc2Zvcm1hdGlvbiBoYXMgY29ycmVjdGVkIHRoZSByaWdodCBza2V3IGluIHRoZSBkYXRhIGZyb20gYmVmb3JlLgoKCmBgYHtyfQpkZl90cmFucyA8LSBkZl9pbXB1dGVkICAjIENvcHkgb3JpZ2luYWwgZGF0YXNldCBhZnRlciBNSUNFCgojZW5zdXJlIGRpYWJldGVzIGlzIG51bWVyaWMgYW5kIGEgZmFjdG9yCmRmX3RyYW5zJGRpYWJldGVzPC0gYXMubnVtZXJpYyhhcy5mYWN0b3IoZGZfdHJhbnMkZGlhYmV0ZXMpKQojIE1pbi1NYXggTm9ybWFsaXphdGlvbgpub3JtYWxpemUgPC0gZnVuY3Rpb24oeCkgewogIGlmIChtYXgoeCkgLSBtaW4oeCkgPT0gMCkgcmV0dXJuKHJlcCgwLCBsZW5ndGgoeCkpKSAgIyBQcmV2ZW50IGRpdmlzaW9uIGJ5IHplcm8KICByZXR1cm4oKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpCn0KCiNhcHBseSBub3JtYWxpemF0aW9uCmRmX3RyYW5zJGJtaSA8LSBub3JtYWxpemUoZGZfaW1wdXRlZCRibWkpCmRmX3RyYW5zJGJsb29kX2dsdWNvc2VfbGV2ZWwgPC0gbm9ybWFsaXplKGRmX2ltcHV0ZWQkYmxvb2RfZ2x1Y29zZV9sZXZlbCkKCiMgWi1TY29yZSBTdGFuZGFyZGl6YXRpb24Kc3RhbmRhcmRpemUgPC0gZnVuY3Rpb24oeCkgewogIHJldHVybigoeCAtIG1lYW4oeCwgbmEucm0gPSBUUlVFKSkgLyBzZCh4LCBuYS5ybSA9IFRSVUUpKQp9CgojYXBwbHkgc3RhbmRhcmRpemF0aW9uCmRmX3RyYW5zJGFnZSA8LSBzdGFuZGFyZGl6ZShkZl90cmFucyRhZ2UpCmRmX3RyYW5zJEhiQTFjX2xldmVsIDwtIHN0YW5kYXJkaXplKGRmX3RyYW5zJEhiQTFjX2xldmVsKQoKIyBBcHBseSBMb2cgVHJhbnNmb3JtYXRpb24gdG8gRml4IFNrZXduZXNzIChpZiBuZWNlc3NhcnkpCmRmX3RyYW5zJGJtaSA8LSBsb2cxcChkZl90cmFucyRibWkpICAjIGxvZygxICsgeCkgYXZvaWRzIGxvZygwKSBpc3N1ZXMKZGZfdHJhbnMkYmxvb2RfZ2x1Y29zZV9sZXZlbCA8LSBsb2cxcChkZl90cmFucyRibG9vZF9nbHVjb3NlX2xldmVsKQoKYm1pMiA8LSBza2V3bmVzcyhkZl90cmFucyRibWkpCmFnZTIgPC0gc2tld25lc3MoZGZfdHJhbnMkYWdlKQpIYkExYzI8LSBza2V3bmVzcyhkZl90cmFucyRIYkExY19sZXZlbCkKZ2x1Y29zZTI8LSBza2V3bmVzcyhkZl90cmFucyRibG9vZF9nbHVjb3NlX2xldmVsKQoKbmV3dGFibGU8LWRhdGEuZnJhbWUoYm1pMixhZ2UyLEhiQTFjMixnbHVjb3NlMikKa2FibGUobmV3dGFibGUsIGNhcHRpb249Ik5ld2x5IFRyYW5zZm9ybWVkIERhdGEiKQoKCmBgYApUcmFuc2Zvcm1pbmcgb3VyIGRhdGEgaGFzIHN1Y2Nlc3NmdWxseSBkZWNyZWFzZWQgc2tld25lc3MgaW4gb3VyIHZhcmlhYmxlcy4gV2UgY2FuIHNlZSBibWkgYW5kIGJsb29kIGdsdWNvc2UgbGV2ZWwgYXJlIG5vIGxvbmdlciBhYm91dCAxLiAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nV2UgY2FuIHNlZSBpbiB0aGUgZ3JhcGhzIGFib3ZlIHRoYXQgYWZ0ZXIgdHJhbnNmb3JtaW5nIHRoZSBkYXRhc2V0LCB0aGUgZGF0YSBzZWVtcyB0byBmb2xsb3cgYSBub3JtYWwgZGlzdHJpYnV0aW9uIG1vcmUgY2xvc2VseS5UaGlzIGlzIGFwcGFyZW50IGluIHRoZSBoaXN0b2dyYW1zIGFwcGVhcmluZyBtb3JlIGluIGEgYmVsbCBzaGFwZSBjdXJ2ZS4nIH0KCiMgQ29tYmluZSBvbGQgYW5kIG5ldyBkYXRhIGZvciBjb21wYXJpc29uCmRmX2NvbXBhcmUgPC0gZGF0YS5mcmFtZSgKICBibWlfb3JpZ2luYWwgPSBkZl9pbXB1dGVkJGJtaSwKICBibWlfdHJhbnNmb3JtZWQgPSBkZl90cmFucyRibWksCiAgYmxvb2RfZ2x1Y29zZV9vcmlnaW5hbCA9IGRmX2ltcHV0ZWQkYmxvb2RfZ2x1Y29zZV9sZXZlbCwKICBibG9vZF9nbHVjb3NlX3RyYW5zZm9ybWVkID0gZGZfdHJhbnMkYmxvb2RfZ2x1Y29zZV9sZXZlbAopCgojIFJlc2hhcGUgZGF0YSBmb3IgZ2dwbG90CmRmX2xvbmcgPC0gbWVsdChkZl9jb21wYXJlKQoKIyBQbG90IGhpc3RvZ3JhbXMKZ2dwbG90KGRmX2xvbmcsIGFlcyh4ID0gdmFsdWUsIGZpbGwgPSB2YXJpYWJsZSkpICsKICBnZW9tX2hpc3RvZ3JhbShhbHBoYSA9IDAuNiwgYmlucyA9IDMwLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICBmYWNldF93cmFwKH4gdmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJDb21wYXJpc29uIG9mIE9yaWdpbmFsIHZzLiBUcmFuc2Zvcm1lZCBEYXRhIiwgeCA9ICJWYWx1ZSIsIHkgPSAiQ291bnQiKQoKCmBgYAoKCgoKIyBBbmFseXppbmcgRGF0YQpUaGUgbmV4dCBzdGVwIGlzIHRvIGZpbmQgd2hpY2ggdmFyaWFibGVzIGFyZSBzaWduaWZpY2FudGx5IGltcG9ydGFudCB3aGVuIHRyeWluZyB0byBwcmVkaWN0IGRpYWJldGVzLiBCeSB1c2luZyBmZWF0dXJlIHNlbGVjdGlvbiwgd2UgYXJlIGFibGUgdG8gcHJlZGljdCBwb3NzaWJsZSBiZXN0IGZpdCBtb2RlbHMgZm9yIG91ciB2YXJpYWJsZSwgZGlhYmV0ZXMuIEZvciB0aGlzIGRhdGEsIHdlIHdpbGwgZm9jdXMgb24gbG9naXN0aWMgcmVncmVzc2lvbi4gVGhlIHZhcmlhYmxlcyBvZiBpbnRlcmVzdCwgZGlhYmV0ZXMsIGlzIGEgYmluYXJ5IHZhcmlhYmxlIGluZGljYXRpbmcgbGluZWFyIHJlZ3Jlc3Npb24gd291bGQgbm90IG1ha2UgYW4gaWRlYWwgbW9kZWwuIAoKCgoqKlJhbmRvbSBGb3Jlc3QqKiAKRmlyc3QsIHdlIHdpbGwgbG9vayBhdCB0aGUgYmVzdCBtb2RlbCBzZWxlY3RlZCBieSBmZWF0dXJlIHNlbGVjdGlvbiB0aHJvdWdoIHJhbmRvbSBmb3Jlc3Q6CmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSxmaWcuY2FwPSdUaGUgdGFibGUgc2hvd3MgdGhlIHNlbGVjdGVkIHZhcmlhYmxlcyByYW5kb20gZm9yZXN0IGhhcyBzZWxlY3RlZCB0byB1c2UgZm9yIG91ciBtb2RlbC4nfQoKIyBQcmVwYXJlIHRoZSBkYXRhCmRmX3RyYW5zJGRpYWJldGVzIDwtIGFzLmZhY3RvcihkZl90cmFucyRkaWFiZXRlcykKCgojIFNldCB1cCBSRkUgY29udHJvbCB3aXRoIHJhbmRvbSBmb3Jlc3QKY29udHJvbCA8LSByZmVDb250cm9sKGZ1bmN0aW9ucyA9IHJmRnVuY3MsIG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQoKIyBBcHBseSBSRkUgdG8gaWRlbnRpZnkgdG9wIGZlYXR1cmVzCnJlc3VsdHMgPC0gcmZlKAogIHggPSBkZl90cmFuc1ssICFjb2xuYW1lcyhkZl90cmFucykgJWluJSAiZGlhYmV0ZXMiXSwKICB5ID0gZGZfdHJhbnMkZGlhYmV0ZXMsCiAgc2l6ZXMgPSBjKDE6OSksCiAgcmZlQ29udHJvbCA9IGNvbnRyb2wKKQoKIyBQcmludCB0aGUgc2VsZWN0ZWQgZmVhdHVyZXMKbW9kZWxzPC1kYXRhLmZyYW1lKHByZWRpY3RvcnMocmVzdWx0cykpCmthYmxlKG1vZGVscywgY2FwdGlvbj0iUmFuZG9tIEZvcmVzdCBTZWxlY3RlZCBNb2RlbCIpCgoKYGBgCioqU3RlcHdpc2UgTG9naXN0aWMgUmVncmVzc2lvbioqCk5leHQsIGxldCdzIGxvb2sgYW5kIHN0ZXB3aXNlIGxvZ2lzdGljIHJlZ3Jlc3Npb24KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIGNvZWZmaWNpZW50cyBzaG93biBiZWxvdyBhcmUgdGhlIG9uZXMgc3RlcHdpc2UgbG9naXN0aWMgcmVncmVzc2lvbiBoYXMgY2hvc2VuIGZvciB0aGUgZmluYWwgbW9kZWwnfQojU3RlcHdpc2UgTG9naXN0aWMgUmVncmVzc2lvbgojIEZpdCBhIGZ1bGwgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB3aXRoIGFsbCBwcmVkaWN0b3JzCmZ1bGxfbW9kZWwgPC0gZ2xtKGRpYWJldGVzIH4gLiwgZGF0YSA9IGRmX3RyYW5zLCBmYW1pbHkgPSBiaW5vbWlhbCkKCiMgUGVyZm9ybSBzdGVwd2lzZSBzZWxlY3Rpb24gKGRlZmF1bHQgaXMgYmFja3dhcmQgZWxpbWluYXRpb24pCnN0ZXBfbW9kZWwxIDwtIHN0ZXAoZnVsbF9tb2RlbCwgZGlyZWN0aW9uID0gImJvdGgiKQpzdW1tYXJ5KHN0ZXBfbW9kZWwxKQpgYGAKCkJvdGggc2VsZWN0aW9uIHR5cGVzIHNlbGVjdGVkIHRoZSBzYW1lIG1vZGVsISBIb3dldmVyIEkgd2lsbCBzdGlsbCBnbyB0aHJvdWdoIHRoZSBzdGVwcyBvZiBjcm9zcy12YWxpZGF0aW9uIGFzIHRob3VnaCBJIHdhcyBjb21wYXJpbmcgdGhlIHR3byBtb2RlbHMuCgojIENyb3NzLVZhbGlkYXRpb24KCkxldCdzIGV4cGxvcmUgY3Jvc3MtdmFsaWRhdGlvbiB0byBzZWUgd2hpY2ggbW9kZWwgaXMgYmVzdCBmaXQuIEZvciBsb2dpc3RpYyByZWdyZXNzaW9uLCBpdCBpcyBtZWFuaW5nZnVsIHRvIGxvb2sgYXQgYWNjdXJhY3kgYW5kIGthcHBhLiAKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQoKI2FkZCBjbGFzc2lmaWNhdGlvbgpkZl90cmFucyRkaWFiZXRlczwtIGZhY3RvcihpZmVsc2UoZGZfdHJhbnMkZGlhYmV0ZXM9PTEsICJZZXMiLCJObyIpKQoKdHJhaW5fY29udHJvbCA8LSB0cmFpbkNvbnRyb2woCiAgbWV0aG9kID0gImN2IiwgICAgICAgICAjIGNyb3NzLXZhbGlkYXRpb24KICBudW1iZXIgPSAxMCwgICAgICAgICAgICMgMTAtZm9sZAogIGNsYXNzUHJvYnMgPSBUUlVFICMgdXNlIGlmIHlvdSdyZSB0cmFja2luZyBBVUMsIFNlbnNpdGl2aXR5LCBldGMuCikKCiNydW4gQ1YgbW9kZWwKY3ZfbW9kZWwgPC0gdHJhaW4oCiAgZGlhYmV0ZXMgfiBoeXBlcnRlbnNpb24gKyBnZW5kZXIgK2FnZSArIEhiQTFjX2xldmVsICsgYmxvb2RfZ2x1Y29zZV9sZXZlbCArIGhlYXJ0X2Rpc2Vhc2UgKyBibWksIAogIGRhdGE9ZGZfdHJhbnMsIAogIG1ldGhvZCA9ICJnbG0iLCAKICBmYW1pbHkgPSAiYmlub21pYWwiLCAKICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sCiAgKQoKcHJpbnQoY3ZfbW9kZWwpCgpgYGAKQWNjdXJhY3kgc2hvd3MgdGhlIG92ZXJhbGwgY29ycmVjdG5lc3Mgb2YgdGhlIG1vZGVsIHByZWRpY3Rpb25zIGFuZCBrYXBwYSBtZWFzdXJlcyB0aGUgbGV2ZWxzIG9mIGFncmVlbWVudCBiZXR3ZWVuIHZhcmlhYmxlcy4gVmFsdWVzIGNsb3NlciB0byAxIHN1Z2dlc3QgaGlnaGVyIGFjY3VyYWN5IGFuZCBiZXR0ZXIgYWdyZWVtZW50LiBUaGlzIG1vZGVsIHN1Z2dlc3RzIGhpZ2ggYWNjdXJhY3kgYW5kIGhpZ2ggYWdyZWVtZW50IGJhc2VkIG9uIHRoZSB2YWx1ZXMuCgojIFJPQyBDdXJ2ZSBhbmQgQU9DIEN1cnZlCgoqKkNvbmZ1c2lvbiBNYXRyaWNlcyoqClJPQyBpcyBhbHNvIGtub3duIGFzIFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpYyBBbmFseXNpcy4gSXQgaXMgYSB0ZWNobmlxdWUgdXNpbmcgZ3JhcGhzIHRoYXQgZXZhbHVhdGVzIHRoZSBwZXJmb3JtYW5jZSBvZiBhIGJpbmFyeSBtb2RlbC4gQmVmb3JlIHBsb3R0aW5nIHRoZSBST0MgY3VydmUsIGl0IGlzIG5lY2Vzc2FyeSB0byBjYWxjdWxhdGUgdGhlIHRydWUgcG9zaXRpdmUgcmF0ZSAoVFBSKSBhbmQgZmFsc2UgcG9zaXRpdmUgcmF0ZSAoRlBSKS4gQmVsb3csIGFyZSBjb25mdXNpb24gbWF0cmljZXMgdGhhdCB3ZXJlIHVzZWQgdG8gY2FsY3VsYXRlIHRob3NlIHZhbHVlcy4KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCdUaGVzZSBtYXRyaWNlcyBhcmUgdXNlZCB0byBjYWxjdWxhdGUgVFBSIGFuZCBGUFIuIFRoZXNlIHZhbHVlcyBhcmUgdXNlZCB0byBjcmVhdGUgdGhlIFJPQyBjdXJ2ZS4nfQoKI1JPQyBhbmQgQVVDCmRmX3RyYW5zJGRpYWJldGVzIDwtIGFzLmZhY3RvcihkZl90cmFucyRkaWFiZXRlcykKCiMgZml0IGEgbG9naXN0aWMKbW9kZWwubG9naXQgPC0gZ2xtKGRpYWJldGVzIH4gYWdlICsgZ2VuZGVyKyBibWkraHlwZXJ0ZW5zaW9uICsgaGVhcnRfZGlzZWFzZSArIGJsb29kX2dsdWNvc2VfbGV2ZWwrIEhiQTFjX2xldmVsICwgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSBkZl90cmFucykKIyBwcmVkaWN0IHByb2JhYmlsaXR5IG9mIFAoWSA9ICJZZXMiKQpwcm9iYWJpbGl0aWVzIDwtIHJvdW5kKGFzLnZlY3RvcihwcmVkaWN0KG1vZGVsLmxvZ2l0LCB0eXBlID0gInJlc3BvbnNlIikpLDMpCiMKdGhyZXNob2xkcyA8LSBjKDAuMCwgMC4yNSwgMC41LCAwLjc1LCAxLjApCgojIExvb3AgdGhyb3VnaCB0aHJlc2hvbGRzIGFuZCBjcmVhdGUgY29uZnVzaW9uIG1hdHJpY2VzCmZvciAodGhyZXNob2xkIGluIHRocmVzaG9sZHMpIHsKICBjYXQoIlxuQ29uZnVzaW9uIE1hdHJpeCBmb3IgVGhyZXNob2xkID0iLCB0aHJlc2hvbGQsICJcbiIpCiAgCiAgIyBDb252ZXJ0IHByb2JhYmlsaXRpZXMgdG8gcHJlZGljdGlvbnMKICAjIGFtOiAxID0gbWFudWFsIHRyYW5zbWlzc2lvbiwgMCA9IGF1dG9tYXRpYyB0cmFuc21pc3Npb24KICBwcmVkaWN0aW9ucyA8LSBpZmVsc2UocHJvYmFiaWxpdGllcyA+IHRocmVzaG9sZCwgIlllcyIsICJObyIpCiAgICBhbGxfbGV2ZWxzIDwtIHVuaW9uKGxldmVscyhmYWN0b3IoZGZfdHJhbnMkZGlhYmV0ZXMpKSwgbGV2ZWxzKGZhY3RvcihwcmVkaWN0aW9ucykpKQogICAgIyBHZW5lcmF0ZSBjb25mdXNpb24gbWF0cml4CiAgY20gPC0gY29uZnVzaW9uTWF0cml4KGZhY3RvcihwcmVkaWN0aW9ucywgbGV2ZWxzPWFsbF9sZXZlbHMpLCBmYWN0b3IoZGZfdHJhbnMkZGlhYmV0ZXMsbGV2ZWxzPWFsbF9sZXZlbHMpLCBwb3NpdGl2ZSA9ICJZZXMiKQogIHByaW50KGNtJHRhYmxlKQp9CgpgYGAKKipST0MgQ3VydmUqKgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsJ1RoZSBwbG90IHNob3dzIHRoZSBwYXRoIG9mIHRoZSBST0MgY3VydmUuJ30KCiNjcmVhdGUgUk9DIGN1cnZlCgpUUFIgPSBjKDEsOTE1MDAvKDkxNTAwKzApLCA5MTM1Ni8oOTEzNTYrMTQ0KSwgOTA2MzIvKDkwNjMyKzg2OCksIDg4MTYwLyg4ODE2MCszMzQwKSwgMC8oOTE1MDArMCkpCkZQUiA9IGMoMSwzNTIvKDM1Mis4MTQ4KSwgNDI4Mi8oNDI4Mis0MjE4KSwgMzE5My8oNTMwNyszMTkzKSwgMjE3OC8oMjE3OCs2MzIyKSwgMC8oODUwMCswKSkKcGxvdChGUFIsIFRQUiwgdHlwZSA9ICJiIiwgbWFpbiA9ICJBbiBJbGx1c3RyYXRpdmUgUk9DIEN1cnZlIiwgY29sID0iYmx1ZSIsCiAgICAgeGxhYj0iMSAtIFNwZWNpZml0eSAoRlBSKSIsIHlsYWIgPSAiU2Vuc2l0aXZpdHkgKFRQUikiKQojIGFkZCBhIG9mZi1kaWFnb25hbCByZXByZXNlbnRpbmcgcmFuZG9tIGd1ZXNzIGFsZ29yaXRobSBpbiBiaW5hcnkgcHJlZGljdGlvbgphYmxpbmUoMCwxLCBsdHkgPSAyLCBjb2wgPSAicmVkIikKIyBsZWdlbmQKbGVnZW5kKCJib3R0b21yaWdodCIsIGMoIkxvZ2lzdGljIE1vZGVsIiwgIlJhbmRvbSBHdWVzcyIpLAogICAgICAgY29sPWMoImJsdWUiLCAicmVkIiksIGx0eSA9IDE6MiwgYnR5PSJuIiwgY2V4ID0gMC45KQpgYGAKKipBVUMgKEFyZWEgdW5kZXIgdGhlIEN1cnZlKSoqCkFVQyBxdWFudGlmaWVzIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgUk9DIGN1cnZlIGludG8gYSBzaW5nbGUgdmFsdWUgdGhhdCByYW5nZXMgZnJvbSAwIHRvIDEuIFRoZSBwbG90IGJlbG93IHVzZXMgdGhlIFJpZW1hbm4gU3VtIGFwcHJveGltYXRpb24gdG8gZXN0aW1hdGUgdGhlIGFyZWEgdW5kZXIgdGhlIGN1cnZlLgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsJ1RoZSBwbG90IGVzdGltYXRlcyB0aGUgYXJlYSBvZiB0aGUgUk9DIEN1cnZlLiBUaGUgcmVnaW9uIGlzIGRpdmlkZWQgaW50byBzdWJyZWdpb25zLCBBLEIsIGFuZCBDLiBUaGVzZSB2YWx1ZXMgYXJlIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZS4nIH0KI0FVQwpUUFIgPSByb3VuZChjKDEsOTE1MDAvKDkxNTAwKzApLCA5MTM1Ni8oOTEzNTYrMTQ0KSwgOTA2MzIvKDkwNjMyKzg2OCksIDg4MTYwLyg4ODE2MCszMzQwKSwgMC8oOTE1MDArMCkpLDMpCkZQUiA9IHJvdW5kKGMoMSwzNTIvKDM1Mis4MTQ4KSwgNDI4Mi8oNDI4Mis0MjE4KSwgMzE5My8oNTMwNyszMTkzKSwgMjE3OC8oMjE3OCs2MzIyKSwgMC8oODUwMCswKSksMykKVFBSMCA9IFRQUls3OjFdCkZQUjAgPSBGUFJbNzoxXQojcGxvdChGUFIwLCBUUFIwLCB0eXBlID0gImIiKQpkYXRTZW5TcGUgPSBkYXRhLmZyYW1lKFRQUjAsIEZQUjApCmdnUk9DID0gZ2dwbG90KGRhdGEgPSBkYXRTZW5TcGUsIGFlcyh4ID0gRlBSMCwgeT1UUFIwKSkgKwogICAgICAgIGdlb21fbGluZShjb2wgPSAic3RlZWxibHVlIikgKwogICAgICAgIGdlb21fcG9pbnQoY29sID0gInJlZCIpICsKICAgICAgICBnZW9tX3NlZ21lbnQoeCA9IEZQUjAsIHkgPSAwLCB4ZW5kID0gRlBSMCwgeWVuZCA9IFRQUjAsIGNvbG9yID0gNCkgKwogICAgICAgIGdlb21fc2VnbWVudCh4ID0gMCwgeSA9IDAsIHhlbmQgPSBGUFIwWzddLCB5ZW5kID0gMCwgY29sb3IgPSA2KSArCiAgICAgICAgZ2d0aXRsZSgiQXBwcm94aW1hdGluZyB0aGUgQVVDIG9mIExvZ2lzdGljIE1vZGVsIikgKwogICAgICAgIHhsYWIoIjEtc3BlY2lmaWNpdHkgKEZQUikiKSArIAogICAgICAgIHlsYWIoIlNlbnNpdGl2aXR5IChUUFIpIikgKwogICAgICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDAuMDI1LCB5ID0gMC4xMjUsIGxhYmVsPSAiQSIpICsgCiAgICAgICAgYW5ub3RhdGUoInRleHQiLCB4ID0gMC4xMDUsIHkgPSAwLjUsIGxhYmVsID0gIkIiKSArCiAgICAgICAgYW5ub3RhdGUoInRleHQiLCB4ID0gMC42MDUsIHkgPSAwLjUsIGxhYmVsID0gIkMiKSArCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjgsIDAuMiksCiAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMC4xNSwgMC4xNSwgMC43NSwgMC4xNSksICJpbmNoZXMiKSwKICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDIsIGNvbG91ciA9ICJuYXZ5IiwgbGluZXR5cGU9MSkpCiMgcGFydGl0aW9uIHRoZSByZWdpb24gdW5kZXIgdGhlIFJPQyBpbnRvIHRyYXBlem9pZHMKZ2dwbG90bHkoZ2dST0MpCmBgYAoKKipBVUMgVmFsdWUqKgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsJ1RoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSBpcyAwLjk3OTUgd2hpY2ggaXMgY2xvc2UgdG8gMSwgdGhpcyBpbmRpY2F0ZXMgYW4gaWRlYWwgbW9kZWwuJ30KQTwtKDAuMDQxKjEpLzIKQjwtICgoMC41MDQtMC4wNDEpKjEpCkM8LSAoKDEtMC41MDQpKjEpCgpBVUM8LSBBK0IrQwprYWJsZShBVUMsIGNhcHRpb249ICJBcmVhIHVuZGVyIHRoZSBDdXJ2ZSIpCgpgYGAKIyBDb25jbHVzaW9uIGZvciBNb2RlbCBGaXQKT3VyIGJlc3QgZml0IG1vZGVsIGlzIDoKJCQKXHRleHR7IERpYWJldGVzfSA9IDkuOTkgLSAwLjI3XHRpbWVzIFx0ZXh0e2dlbmRlcn0gLSAxLjAzXHRpbWVzIFx0ZXh0e2FnZX0gIC0wLjhcdGltZXMgXHRleHR7aHlwZXJ0ZW5zaW9ufSAtIDAuNzZcdGltZXMgXHRleHR7aGVhcnQgZGlzZWFzZX0gLTEwLjI0XHRpbWVzIFx0ZXh0e2JtaX0gLSAyLjQ5XHRpbWVzIFx0ZXh0e0hiQTFjIGxldmVsfSAtIC0xMC4zXHRpbWVzIFx0ZXh0e2Jsb29kIGdsdWNvc2UgbGV2ZWx9CiQkCgoKVGhlIGJlc3QgZml0IG1vZGVsIHdhcyBzZWxlY3RlZCBiYXNlZCBvbiBhIHZhcmlldHkgb2YgZGlmZmVyZW50IGZlYXR1cmVzLiBCb3RoIHN0ZXB3aXNlIHNlbGVjdGlvbiBhbmQgcmFuZG9tIGZvcmVzdCBzZWxlY3RlZCB0aGUgbW9kZWwgYXMgYmVzdCBmaXQuIEFmdGVyIHJ1bm5pbmcgcGVyZm9ybWFuY2UgYW5hbHlzZXMgb24gdGhlIG1vZGVsLCB0aGUgbW9kZWwgd2FzIHNob3duIHRvIGJlIG9mIGhpZ2ggcGVyZm9ybWFuY2UuIFRoaXMgbWVhbnMgdGhhdCB0aGUgdmFyaWFibGVzIGNob3NlbiBmb3IgdGhlIGZpbmFsIG1vZGVsIGFyZSBzaWduaWZpY2FudCBmb3IgcHJlZGljdGluZyBkaWFiZXRlcy4gVGhlIG1vZGVsIGNob3NlIG5vdCB0byBzZWxlY3Qgc21va2luZyBoaXN0b3J5IGFzIHNpZ25pZmljYW50IHByZWRpY3RvciBmb3IgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiBBIHBvc3NpYmxlIHN1Z2dlc3Rpb24gZm9yIHRoaXMgd291bGQgYmUgdG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgcmVzcG9uc2UgdmFsdWVzIHRvIGEgYmluYXJ5IHJlc3BvbnNlLiBTdWNoIGFzLCAiSGF2ZSB5b3UgZXZlciBzbW9rZWQ/IiBhbmQgaGF2ZSB0aGUgcmVzcG9uc2UgYmUgIlllcyIgb3IgIk5vIi4gVGhpcyBjb3VsZCBoZWxwIGluIHByZWRpY3RpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNtb2tpbmcgYW5kIGRpYWJldGVzLiAK